Load libraries

library(Seurat)
library(princurve)
library(monocle)
library(gprofiler2)
library(seriation)
library(Matrix)
library(dplyr)
library(RColorBrewer)
library(ggplot2)
library(ggExtra)
library(cowplot)
library(wesanderson)

#Set ggplot theme as classic
theme_set(theme_classic())

Load the full dataset

Hem.data <- readRDS("../QC.filtered.clustered.cells.RDS")
DimPlot(object = Hem.data,
        group.by = "Cell_ident",
        reduction = "spring",
        cols = c("#ebcb2e", #"CR"
            "#e7823a", #"ChP"
            "#4cabdc", # Chp_prog
            "#68b041", #"Dorso-Medial_pallium" 
            "#e46b6b", #"Hem" 
            "#e3c148", #"Medial_pallium"
            "#046c9a", # Pallial
            "#4990c9"#"Thalamic_eminence"
                     )
        )

Hem.data$Lineage <- sapply(Hem.data$Cell_ident,
                              FUN = function(x) {
                                if (x %in% c("Cajal-Retzius_neurons", "Hem")) {
                                  x = "Cajal-Retzius_neurons"
                                } else if (x %in% c("Pallial_neurons", "Medial_pallium")) {
                                  x = "Pallial_neurons"
                                } else if(x %in% c("ChP", "ChP_progenitors")) {
                                   x =  "Choroid_Plexus"
                                } else {
                                  x = "other"
                                  }
                              })
DimPlot(Hem.data,
        reduction = "spring",
        group.by = "Lineage",
        pt.size = 1,
        cols =  c("#cc391b","#e7823a","#969696","#026c9a")
        ) + NoAxes()

Differentiating neurons trajectory

Neurons.data <-  subset(Hem.data, idents = c("Cajal-Retzius_neurons", "Pallial_neurons"))

DimPlot(Neurons.data ,
        reduction = "spring",
        pt.size = 1,
        cols =  c("#cc391b","#026c9a")
        ) + NoAxes()

Fit principale curve on the two lineages

Cajal-Retzius cells

Trajectories.Hem <- Neurons.data@meta.data %>%
                    select("Barcodes", "nUMI", "Spring_1", "Spring_2", "AP_signature1","BP_signature1", "EN_signature1", "LN_signature1", "Lineage") %>%
                    filter(Lineage == "Cajal-Retzius_neurons")
fit <- principal_curve(as.matrix(Trajectories.Hem[,c("Spring_1", "Spring_2")]),
                       smoother='lowess',
                       trace=TRUE,
                       f = .7,
                       stretch=0)
## Starting curve---distance^2: 45804778678
## Iteration 1---distance^2: 27732113
## Iteration 2---distance^2: 27728318
#The principal curve smoothed
Hem.pc.line <- as.data.frame(fit$s[order(fit$lambda),]) 

#Pseudotime score
Trajectories.Hem$PseudotimeScore <- fit$lambda/max(fit$lambda)
if (cor(Trajectories.Hem$PseudotimeScore, Neurons.data@assays$SCT@data['Hmga2', Trajectories.Hem$Barcodes]) > 0) {
  Trajectories.Hem$PseudotimeScore <- -(Trajectories.Hem$PseudotimeScore - max(Trajectories.Hem$PseudotimeScore))
}

Pallial neurons

Trajectories.Pallial <- Neurons.data@meta.data %>%
                        select("Barcodes", "nUMI", "Spring_1", "Spring_2", "AP_signature1","BP_signature1", "EN_signature1", "LN_signature1", "Lineage") %>%
                        filter(Lineage == "Pallial_neurons")
fit <- principal_curve(as.matrix(Trajectories.Pallial[,c("Spring_1", "Spring_2")]),
                       smoother='lowess',
                       trace=TRUE,
                       f = .7,
                       stretch=0)
## Starting curve---distance^2: 26984853690
## Iteration 1---distance^2: 22153700
## Iteration 2---distance^2: 22179462
## Iteration 3---distance^2: 22180297
#The principal curve smoothed
Pallial.pc.line <- as.data.frame(fit$s[order(fit$lambda),])

#Pseudotime score
Trajectories.Pallial$PseudotimeScore <- fit$lambda/max(fit$lambda)
if (cor(Trajectories.Pallial$PseudotimeScore, Neurons.data@assays$SCT@data['Hmga2', Trajectories.Pallial$Barcodes]) > 0) {
  Trajectories.Pallial$PseudotimeScore <- -(Trajectories.Pallial$PseudotimeScore - max(Trajectories.Pallial$PseudotimeScore))
}

Combine the two trajectories’ data

Trajectories.neurons <- rbind(Trajectories.Pallial, Trajectories.Hem)
cols <- brewer.pal(n =11, name = "Spectral")

ggplot(Trajectories.neurons, aes(Spring_1, Spring_2)) +
  geom_point(aes(color=PseudotimeScore), size=2, shape=16) + 
  scale_color_gradientn(colours=rev(cols), name='Speudotime score') +
  geom_line(data=Pallial.pc.line, color="#026c9a", size=0.77) +
  geom_line(data=Hem.pc.line, color="#cc391b", size=0.77)

Plot pan-neuronal genes along this axis

Neurons.data <- NormalizeData(Neurons.data, normalization.method = "LogNormalize", scale.factor = 10000, assay = "RNA")
# Neurog2
p1 <- FeaturePlot(object = Neurons.data,
            features = c("Neurog2"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()

Trajectories.neurons$Neurog2 <- Neurons.data@assays$RNA@data["Neurog2", Trajectories.neurons$Barcodes]

p2 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore, y= Neurog2)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(method="loess", n= 50, aes(color= Lineage)) +
        ylim(0,NA)

# Tbr1 
p3 <- FeaturePlot(object = Neurons.data ,
            features = c("Tbr1"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()
Trajectories.neurons$Tbr1 <- Neurons.data@assays$RNA@data["Tbr1", Trajectories.neurons$Barcodes]

p4 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore, y= Tbr1)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(method="loess", n= 50, aes(color= Lineage)) +
        ylim(0,NA)

# Mapt 
p5 <- FeaturePlot(object = Neurons.data ,
            features = c("Mapt"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()

Trajectories.neurons$Mapt <- Neurons.data@assays$RNA@data["Mapt", Trajectories.neurons$Barcodes]

p6 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore, y= Mapt)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(method="loess", n= 50, aes(color= Lineage)) +
        ylim(0,NA)

p1 + p2 + p3 + p4 + p5 + p6 + patchwork::plot_layout(ncol = 2)

Shift Pseudotime in both lineage

Since we observe the first 25% of both trajectories are occupied by few, likely progenitor cells, we shift this cell along the axis

Pseudotime.intervals <- Trajectories.neurons%>%
                          select(Lineage, PseudotimeScore) %>%
                          mutate(Pseudotime.bins = cut(Trajectories.neurons$PseudotimeScore, seq(0, max(Trajectories.neurons$PseudotimeScore) + 0.05, 0.05), dig.lab = 2, right = FALSE)) %>%
                          group_by(Lineage, Pseudotime.bins) %>%
                          summarise(n=n())

ggplot(Pseudotime.intervals, aes(x=Pseudotime.bins, y=n, fill=Lineage)) +
        geom_bar(stat = "identity", width = 0.90) +
        theme(axis.text.x = element_text(angle = 45, hjust=1))+
        scale_fill_manual(values= c("#cc391b", "#026c9a"))

score <- sapply(Trajectories.neurons$PseudotimeScore,
                FUN = function(x) if (x <= 0.2) {x= 0.2} else { x=x })

Trajectories.neurons$PseudotimeScore.shifted <- (score - min(score)) / (max(score) - min(score))
# Neurog2
p1 <- FeaturePlot(object = Neurons.data ,
            features = c("Neurog2"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()

p2 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore.shifted, y= Neurog2)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(method="loess", n= 50, aes(color= Lineage)) +
        ylim(0,NA)

# Tbr1 
p3 <- FeaturePlot(object = Neurons.data ,
            features = c("Tbr1"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()

p4 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore.shifted, y= Tbr1)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(method="loess", n= 50, aes(color= Lineage)) +
        ylim(0,NA)

# Mapt 
p5 <- FeaturePlot(object = Neurons.data ,
            features = c("Mapt"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()

p6 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore.shifted, y= Mapt)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(method="loess", n= 50, aes(color= Lineage)) +
        ylim(0,NA)

p1 + p2 + p3 + p4 + p5 + p6 + patchwork::plot_layout(ncol = 2)

ggplot(Trajectories.neurons, aes(x= PseudotimeScore.shifted, y= nUMI/10000)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(method="loess", n= 50, aes(color= Lineage)) +
        ylim(0,NA)

rm(list = ls()[!ls() %in% c("Trajectories.neurons")])

Load progenitors with cell cycle trajectory fitted

Progenitors.data <- readRDS("../ProgenitorsDiversity/Progenitors.RDS")
table(Progenitors.data$Cell_ident)
## 
## Dorso-Medial_pallium                  Hem       Medial_pallium 
##                 3451                 1954                 2719

To balance the number of progenitors in both domain we will only work with Hem and Medial_pallium annotated cells. Since we are using pallial cell to contrast CR specific trajectory we think this approximation will not significantly affect our analysis.

Progenitors.data <-  subset(Progenitors.data, idents = c("Hem", "Medial_pallium"))
p1 <- DimPlot(Progenitors.data,
        reduction = "spring",
        pt.size = 0.5,
        cols =  c("#e3c148", "#e46b6b")) + NoAxes()

p2 <- FeaturePlot(object = Progenitors.data,
            features = "Revelio.cc",
            pt.size = 0.5,
            cols = rev(brewer.pal(10,"Spectral")),
            reduction = "spring",
            order = T) & NoAxes()

p3 <- DimPlot(object = Progenitors.data,
        group.by = "Revelio.phase",
        pt.size = 0.5,
        reduction = "spring",
        cols =  c(wes_palette("FantasticFox1")[1:3],"grey40",wes_palette("FantasticFox1")[5])) & NoAxes()

p1 + p2 + p3  + patchwork::plot_layout(ncol = 2)

Combined progenitors and neurons along Pseudotime

# Start with neurons data
Trajectories.all <- Trajectories.neurons %>% select(Barcodes, nUMI, Spring_1, Spring_2, AP_signature1, BP_signature1, EN_signature1, LN_signature1, Lineage)

Trajectories.all$Pseudotime <- Trajectories.neurons$PseudotimeScore.shifted + 0.5
Trajectories.all$Phase <- NA
# Add progenitors data
Trajectories.progenitors <- Progenitors.data@meta.data %>%
                              select(Barcodes, nUMI, Spring_1, Spring_2, AP_signature1, BP_signature1, EN_signature1, LN_signature1) %>% 
                              mutate(Lineage= ifelse(Progenitors.data$Cell_ident == "Medial_pallium", "Pallial_neurons", "Cajal-Retzius_neurons") ,
                                     Pseudotime= Progenitors.data$Revelio.cc/2,
                                     Phase = Progenitors.data$Revelio.phase)
Trajectories.all <- rbind(Trajectories.all, Trajectories.progenitors)

Trajectories.all$Phase <- factor(Trajectories.all$Phase, levels = c("G1.S", "S", "G2", "G2.M", "M.G1"))
p1 <- ggplot(Trajectories.all, aes(Spring_1, Spring_2)) +
        geom_point(aes(color=Pseudotime), size=0.5) + 
        scale_color_gradientn(colours=rev(brewer.pal(n =11, name = "Spectral")), name='Speudotime score')

p2 <- ggplot(Trajectories.all, aes(Spring_1, Spring_2)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a"))

p1 + p2

p1 <- ggplot(Trajectories.all, aes(x= Pseudotime, y= nUMI/10000)) +
        geom_point(aes(color= Phase), size=0.5) +
        scale_color_manual(values= c(wes_palette("FantasticFox1")[1:3],"grey40",wes_palette("FantasticFox1")[5])) +
        geom_smooth(method="loess", n= 50, fill="grey") +
        ylim(0,NA)

p2 <- ggplot(Trajectories.all, aes(x= Pseudotime, y= nUMI/10000)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(method="loess", n= 50, fill="grey") +
        ylim(0,NA)

p1 / p2

p1 <- ggplot(Trajectories.all, aes(x= Pseudotime, y= AP_signature1)) +
  geom_point(aes(color= Lineage), size=0.5) +
  scale_color_manual(values= c("#cc391b", "#026c9a")) +
  geom_smooth(method="loess", n= 50, fill="grey")


p2 <- ggplot(Trajectories.all, aes(x= Pseudotime, y= BP_signature1)) +
  geom_point(aes(color= Lineage), size=0.5) +
  scale_color_manual(values= c("#cc391b", "#026c9a")) +
  geom_smooth(method="loess", n= 50, fill="grey")

p3 <- ggplot(Trajectories.all, aes(x= Pseudotime, y= EN_signature1)) +
  geom_point(aes(color= Lineage), size=0.5) +
  scale_color_manual(values= c("#cc391b", "#026c9a")) +
  geom_smooth(method="loess", n= 50, fill="grey")

p4 <- ggplot(Trajectories.all, aes(x= Pseudotime, y= LN_signature1)) +
  geom_point(aes(color= Lineage), size=0.5) +
  scale_color_manual(values= c("#cc391b", "#026c9a")) +
  geom_smooth(method="loess", n= 50, fill="grey")


p1 / p2 / p3 / p4

rm(list = ls()[!ls() %in% c("Trajectories.all")])

Subset the full dataset Seurat object

Hem.data <- readRDS("../QC.filtered.clustered.cells.RDS")
Neuro.trajectories <- CreateSeuratObject(counts = Hem.data@assays$RNA@data[, Trajectories.all$Barcodes],
                                         meta.data = Trajectories.all)

spring <- as.matrix(Neuro.trajectories@meta.data %>% select("Spring_1", "Spring_2"))
  
Neuro.trajectories[["spring"]] <- CreateDimReducObject(embeddings = spring, key = "Spring_", assay = DefaultAssay(Neuro.trajectories))
p1 <- FeaturePlot(object = Neuro.trajectories,
            features = "Pseudotime",
            pt.size = 0.5,
            cols = rev(colorRampPalette(brewer.pal(n =11, name = "Spectral"))(100)),
            reduction = "spring",
            order = T) & NoAxes()

p2 <- DimPlot(object = Neuro.trajectories,
        group.by = "Lineage",
        pt.size = 0.5,
        reduction = "spring",
        cols =  c("#cc391b", "#026c9a")) & NoAxes()


p3 <- DimPlot(object = Neuro.trajectories,
        group.by = "Phase",
        pt.size = 0.5,
        reduction = "spring",
        cols =  c(wes_palette("FantasticFox1")[1:3],"grey40",wes_palette("FantasticFox1")[5])) & NoAxes()

p1 + p2 + p3

rm(list = ls()[!ls() %in% c("Neuro.trajectories")])

Normalization

Neuro.trajectories<- NormalizeData(Neuro.trajectories, normalization.method = "LogNormalize", scale.factor = 10000, assay = "RNA")
Neuro.trajectories <- FindVariableFeatures(Neuro.trajectories, selection.method = "disp", nfeatures = 3000, assay = "RNA")

Plot some genes along pseudotime

source("../Functions/functions_GeneTrends.R")

Plot.Genes.trend(Seurat.data= Neuro.trajectories,
                 group.by = "Lineage",
                 genes= c("Gas1","Sox2",
                          "Neurog2", "Btg2",
                          "Tbr1", "Mapt",
                          "Trp73", "Foxg1"))

Plot.Genes.trend(Seurat.data= Neuro.trajectories,
                 group.by = "Lineage",
                 genes= c("Gmnc", "Mcidas",
                          "Foxj1", "Trp73",
                          "Lhx1", "Cdkn1a"))

Plot.Genes.trend(Seurat.data= Neuro.trajectories,
                 group.by = "Lineage",
                 genes= c("Mki67", "Top2a",
                          "H2afx", "Cdkn1c"))

Use monocle2 to model gene expression along cycling axis

Initialize a monocle object

# Transfer metadata
meta.data <- data.frame(Barcode= Neuro.trajectories$Barcodes,
                        Lineage= Neuro.trajectories$Lineage,
                        Pseudotime= Neuro.trajectories$Pseudotime,
                        Phase= Neuro.trajectories$Phase)

Annot.data  <- new('AnnotatedDataFrame', data = meta.data)

# Transfer counts data
var.genes <- Neuro.trajectories[["RNA"]]@var.features
count.data = data.frame(gene_short_name = rownames(Neuro.trajectories[["RNA"]]@data[var.genes,]),
                        row.names = rownames(Neuro.trajectories[["RNA"]]@data[var.genes,]))

feature.data <- new('AnnotatedDataFrame', data = count.data)

# Create the CellDataSet object including variable genes only
gbm_cds <- newCellDataSet(Neuro.trajectories[["RNA"]]@counts[var.genes,],
                          phenoData = Annot.data,
                          featureData = feature.data,
                          lowerDetectionLimit = 0,
                          expressionFamily = negbinomial())
gbm_cds <- estimateSizeFactors(gbm_cds)
gbm_cds <- estimateDispersions(gbm_cds)
gbm_cds <- detectGenes(gbm_cds, min_expr = 0.1)
rm(list = ls()[!ls() %in% c("Neuro.trajectories", "gbm_cds", "Gene.Trend", "Plot.Genes.trend")])
gc()
##             used  (Mb) gc trigger   (Mb)  max used   (Mb)
## Ncells   3552949 189.8    6202435  331.3   6202435  331.3
## Vcells 102154636 779.4  399886019 3050.9 618405383 4718.1

Find Pan-neuronal genes

# Split pallial and subpallial cells for gene expression fitting
#Pallial cells
Pallialcells <- Neuro.trajectories@meta.data %>%
                filter(Lineage == "Pallial_neurons") %>%
                pull(Barcodes)

# Cajal-Retzius cells
CRcells <- Neuro.trajectories@meta.data %>%
                   filter(Lineage == "Cajal-Retzius_neurons") %>%
                   pull(Barcodes)
# We filter-out genes detected in less than 200 or 200 cells along Pallial or CR lineages
num.cells <- Matrix::rowSums(Neuro.trajectories@assays$RNA@counts[,Pallialcells] > 0)
Pallial.expressed <- names(x = num.cells[which(x = num.cells >= 200)])

num.cells <- Matrix::rowSums(Neuro.trajectories@assays$RNA@counts[,CRcells] > 0)
CR.expressed <- names(x = num.cells[which(x = num.cells >= 200)])

Input.genes <- rownames(gbm_cds)[rownames(gbm_cds) %in% intersect(Pallial.expressed, CR.expressed)]
Pallial.genes <- differentialGeneTest(gbm_cds[Input.genes, Pallialcells], 
                                                 fullModelFormulaStr = "~sm.ns(Pseudotime, df = 3)", 
                                                 reducedModelFormulaStr = "~1", 
                                                 cores = parallel::detectCores() - 2)

#Filter based on FDR
Pallial.genes.filtered <- Pallial.genes  %>% filter(qval < 1e-3)
CRcells.genes <- differentialGeneTest(gbm_cds[Input.genes, CRcells], 
                                                 fullModelFormulaStr = "~sm.ns(Pseudotime, df = 3)", 
                                                 reducedModelFormulaStr = "~1", 
                                                 cores = parallel::detectCores() - 2)

#Filter based on FDR
CRcells.genes.filtered <- CRcells.genes  %>% filter(qval < 1e-3)
Common.genes <- intersect(Pallial.genes.filtered$gene_short_name, CRcells.genes.filtered$gene_short_name)
# Smooth genes expression along the two trajectories
nPoints <- 200

new_data = list()
for (Lineage in unique(pData(gbm_cds)$Lineage)){
  new_data[[length(new_data) + 1]] = data.frame(Pseudotime = seq(min(pData(gbm_cds)$Pseudotime), max(pData(gbm_cds)$Pseudotime), length.out = nPoints), Lineage=Lineage)
}

new_data = do.call(rbind, new_data)

# Smooth gene expression
curve_matrix <- genSmoothCurves(gbm_cds[as.character(Common.genes),],
                                trend_formula = "~sm.ns(Pseudotime, df = 3)*Lineage",
                                relative_expr = TRUE,
                                new_data = new_data,
                                cores= parallel::detectCores() - 2)
# Extract genes with person's cor > 0.6 between the 2 trajectories

Pallial.smoothed <- scale(t(curve_matrix[,c(1:200)]))  #Pallial points
CR.smoothed <- scale(t(curve_matrix[,c(201:400)])) #CR points

mat <- cor(Pallial.smoothed, CR.smoothed, method = "pearson")

Gene.Cor <- diag(mat)
hist(Gene.Cor, breaks = 100)
abline(v=0.8,col=c("blue"))

PanNeuro.genes <- names(Gene.Cor[Gene.Cor > 0.8])
# Order rows using seriation
dst <- as.dist((1-cor(scale(t(curve_matrix[PanNeuro.genes,c(400:201)])), method = "pearson")))
row.ser <- seriate(dst, method ="MDS_angle") #MDS_angle
gene.order <- PanNeuro.genes[get_order(row.ser)]

anno.colors <- list(lineage = c(Pallial="#026c9a",CR="#cc391b"))


pheatmap::pheatmap(curve_matrix[rev(gene.order),
                                c(1:200, 201:400)], #CR
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_col = data.frame(lineage = rep(c("Pallial","CR"), each=200)),
                   annotation_colors = anno.colors,
                   show_colnames = F,
                   show_rownames = T,
                   fontsize_row = 2,
                   color =  viridis::viridis(10),
                   breaks = seq(-2.5,2.5, length.out = 10),
                   main = "")

rm(list = ls()[!ls() %in% c("Neuro.trajectories", "gbm_cds", "Gene.Trend", "Plot.Genes.trend")])
gc()
##             used  (Mb) gc trigger   (Mb)  max used   (Mb)
## Ncells   3600628 192.3    6202435  331.3   6202435  331.3
## Vcells 102265702 780.3  319908816 2440.8 618405383 4718.1

Test each gene trend over pseudotime score

Find genes DE along pseudomaturation axis

pseudo.maturation.diff <- differentialGeneTest(gbm_cds[fData(gbm_cds)$num_cells_expressed > 80,], 
                                                 fullModelFormulaStr = "~sm.ns(Pseudotime, df = 3)*Lineage", 
                                                 reducedModelFormulaStr = "~sm.ns(Pseudotime, df = 3)", 
                                                 cores = parallel::detectCores() - 2)
# Filter genes based on FDR
pseudo.maturation.diff.filtered <- pseudo.maturation.diff %>% filter(qval < 1e-40)

Direction of the DEG by calculating the area between curves (ABC)

Smooth commun genes along the two trajectories

# Create a new pseudo-DV vector of 200 points
nPoints <- 200

new_data = list()
for (Lineage in unique(pData(gbm_cds)$Lineage)){
  new_data[[length(new_data) + 1]] = data.frame(Pseudotime = seq(min(pData(gbm_cds)$Pseudotime), max(pData(gbm_cds)$Pseudotime), length.out = nPoints), Lineage=Lineage)
}

new_data = do.call(rbind, new_data)

# Smooth gene expression
Diff.curve_matrix <- genSmoothCurves(gbm_cds[as.character(pseudo.maturation.diff.filtered$gene_short_name),],
                                      trend_formula = "~sm.ns(Pseudotime, df = 3)*Lineage",
                                      relative_expr = TRUE,
                                      new_data = new_data,
                                      cores= parallel::detectCores() - 2)

Compute the ABC for each gene

# Extract matrix containing smoothed curves for each lineages
Pal_curve_matrix <- Diff.curve_matrix[, 1:nPoints] #Pallial points
CR_curve_matrix <- Diff.curve_matrix[, (nPoints + 1):(2 * nPoints)] #CR points

# Direction of the comparison : postive ABCs <=> Upregulated in CR lineage
ABCs_res <- CR_curve_matrix - Pal_curve_matrix

# Average logFC between the 2 curves
ILR_res <- log2(CR_curve_matrix/ (Pal_curve_matrix + 0.1)) 
  
ABCs_res <- apply(ABCs_res, 1, function(x, nPoints) {
                  avg_delta_x <- (x[1:(nPoints - 1)] + x[2:(nPoints)])/2
                  step <- (100/(nPoints - 1))
                  res <- round(sum(avg_delta_x * step), 3)
                  return(res)},
                  nPoints = nPoints) # Compute the area below the curve
  
ABCs_res <- cbind(ABCs_res, ILR_res[,ncol(ILR_res)])
colnames(ABCs_res)<- c("ABCs", "Endpoint_ILR")

# Import ABC values into the DE test results table
pseudo.maturation.diff.filtered <- cbind(pseudo.maturation.diff.filtered[,1:4],
                                         ABCs_res,
                                         pseudo.maturation.diff.filtered[,5:6])

Cajal-Retzius cells specific trajectory analysis

# Extract Cajal-Retzius expressed genes
CR.res <- as.data.frame(pseudo.maturation.diff.filtered[pseudo.maturation.diff.filtered$ABCs > 0,])
CR.genes <- row.names(CR.res)

CR_curve_matrix <- CR_curve_matrix[CR.genes, ]

Gene expression profiles along the trajectory

## Cluster gene by expression profiles
Pseudotime.genes.clusters <- cluster::pam(as.dist((1 - cor(Matrix::t(CR_curve_matrix),method = "pearson"))), k= 5)

CR.Gene.dynamique <- data.frame(Gene= names(Pseudotime.genes.clusters$clustering),
                                 Waves= Pseudotime.genes.clusters$clustering,
                                 Gene.Clusters = Pseudotime.genes.clusters$clustering,
                                 q.val = CR.res$qval,
                                 ABCs= CR.res$ABCs
                                 ) %>% arrange(Gene.Clusters)

row.names(CR.Gene.dynamique) <- CR.Gene.dynamique$Gene
CR.Gene.dynamique$Gene.Clusters <- paste0("Clust.", CR.Gene.dynamique$Gene.Clusters)
# Order the rows using seriation
dst <- as.dist((1-cor(scale(t(CR_curve_matrix)), method = "pearson")))
row.ser <- seriation::seriate(dst, method ="R2E") #"R2E" #TSP #"GW" "GW_ward"
gene.order <- rownames(CR_curve_matrix[get_order(row.ser),])

# Set annotation colors
pal <- wes_palette("Darjeeling1")
anno.colors <- list(lineage = c(Pallial_neurons="#026c9a", Cajal_Retzius="#cc391b"),
                    Gene.Clusters = c(Clust.1 =pal[1] , Clust.2=pal[2], Clust.3=pal[3], Clust.4=pal[4], Clust.5=pal[5]))


pheatmap::pheatmap(Diff.curve_matrix[gene.order,
                                c(200:1,#Pal 
                                  201:400)], #CR
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = CR.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_col = data.frame(lineage = rep(c("Pallial_neurons","Cajal_Retzius"), each=200)),
                   annotation_colors = anno.colors,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

We manually correct the reordering so genes are aligned from top left to bottom rigth

gene.order <- gene.order[c(245:1,612:246)]

pheatmap::pheatmap(Diff.curve_matrix[gene.order,
                                c(200:1,#Pal 
                                  201:400)], #CR
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = CR.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_col = data.frame(lineage = rep(c("Pallial_neurons","Cajal_Retzius"), each=200)),
                   annotation_colors = anno.colors,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

anno.colors <- list(Cell.state = c(Cycling_RG="#046c9a", Differentiating_cells="#ebcb2e"),
                    Gene.Clusters = c(Clust.1 =pal[1] , Clust.2=pal[2], Clust.3=pal[3], Clust.4=pal[4], Clust.5=pal[5]))

col.anno <- data.frame(Cell.state = rep(c("Cycling_RG","Differentiating_cells"), c(70,130)))
rownames(col.anno) <- 201:400

pheatmap::pheatmap(CR_curve_matrix[gene.order,],
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = CR.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_col = col.anno,
                   annotation_colors = anno.colors,
                   gaps_col = 70,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

### Gene cluster trend

source("../Functions/functions_GeneClusterTrend.R")

Plot.clust.trends(Neuro.trajectories,
                   Lineage = "Cajal-Retzius_neurons",
                   Which.cluster = 1:5,
                   clust.list = Pseudotime.genes.clusters$clustering,
                   Smooth.method = "gam")

GO term enrichment in gene clusters using gprofiler2

CR.gostres <- gost(query = list("Clust.1" = CR.Gene.dynamique %>% filter(Gene.Clusters == "Clust.1") %>% pull(Gene) %>% as.character(),
                             "Clust.2" = CR.Gene.dynamique %>% filter(Gene.Clusters == "Clust.2") %>% pull(Gene) %>% as.character(),
                             "Clust.3" = CR.Gene.dynamique %>% filter(Gene.Clusters == "Clust.3") %>% pull(Gene) %>% as.character(),
                             "Clust.4" = CR.Gene.dynamique %>% filter(Gene.Clusters == "Clust.4") %>% pull(Gene) %>% as.character(),
                             "Clust.5" = CR.Gene.dynamique %>% filter(Gene.Clusters == "Clust.5") %>% pull(Gene) %>% as.character()),
                organism = "mmusculus", ordered_query = F, 
                multi_query = F, significant = T, exclude_iea = T, 
                measure_underrepresentation = F, evcodes = T, 
                user_threshold = 0.05, correction_method = "fdr", 
                domain_scope = "annotated", custom_bg = NULL, 
                numeric_ns = "", sources = c("GO:MF", "GO:BP"), as_short_link = F)

write.table(apply(CR.gostres$result,2,as.character),
            "CR_GO_res-by-waves.csv", sep = ";", quote = F, row.names = F)
DNA_damage_GOterm <- CR.gostres$result[CR.gostres$result$term_id %in% c("GO:0008630", "GO:0030330", "GO:0031571", "GO:0006974", "GO:0006977","GO:0033554",
                                                                                 "GO:0044773", "GO:0042771", "GO:0042770", "GO:2001021", "GO:1902229"),]

DNA_damage_GOterm[,c(1,2,3,5,6,7,11)]
## # A tibble: 12 × 7
##    query   significant p_value query_size intersection_size precision term_name 
##    <chr>   <lgl>         <dbl>      <int>             <int>     <dbl> <chr>     
##  1 Clust.1 TRUE        0.0224         144                19    0.132  cellular …
##  2 Clust.2 TRUE        0.00827         99                17    0.172  cellular …
##  3 Clust.2 TRUE        0.0106          99                 3    0.0303 intrinsic…
##  4 Clust.3 TRUE        0.0140          77                14    0.182  cellular …
##  5 Clust.3 TRUE        0.0193          77                 8    0.104  cellular …
##  6 Clust.4 TRUE        0.0136         140                 4    0.0286 negative …
##  7 Clust.4 TRUE        0.0164         140                 3    0.0214 regulatio…
##  8 Clust.4 TRUE        0.0234         140                20    0.143  cellular …
##  9 Clust.4 TRUE        0.0257         140                 2    0.0143 DNA damag…
## 10 Clust.4 TRUE        0.0372         140                 4    0.0286 intrinsic…
## 11 Clust.4 TRUE        0.0443         140                 3    0.0214 DNA damag…
## 12 Clust.5 TRUE        0.00446         57                12    0.211  cellular …
CR.gostres <- gost(query = as.character(CR.Gene.dynamique$Gene),
                organism = "mmusculus", ordered_query = F, 
                multi_query = F, significant = T, exclude_iea = T, 
                measure_underrepresentation = F, evcodes = T, 
                user_threshold = 0.05, correction_method = "fdr", 
                domain_scope = "annotated", custom_bg = NULL, 
                numeric_ns = "", sources = c("GO:MF", "GO:BP"), as_short_link = F)

write.table(apply(CR.gostres$result,2,as.character),
            "CR_GO_res.csv", sep = ";", quote = F, row.names = F)
DNA_damage_GOterm <- CR.gostres$result[CR.gostres$result$term_id %in% c("GO:0008630", "GO:0030330", "GO:0031571", "GO:0006974", "GO:0006977",
                                                                                 "GO:0044773", "GO:0042771", "GO:0042770", "GO:2001021", "GO:1902229"),]

DNA_damage_GOterm[,c(1,2,3,5,6,7,11)]
## # A tibble: 10 × 7
##    query   significant  p_value query_size intersection_size precision term_name
##    <chr>   <lgl>          <dbl>      <int>             <int>     <dbl> <chr>    
##  1 query_1 TRUE        0.000269        517                11   0.0213  intrinsi…
##  2 query_1 TRUE        0.00136         517                30   0.0580  cellular…
##  3 query_1 TRUE        0.00721         517                 4   0.00774 mitotic …
##  4 query_1 TRUE        0.00783         517                 7   0.0135  mitotic …
##  5 query_1 TRUE        0.00959         517                 6   0.0116  DNA dama…
##  6 query_1 TRUE        0.0105          517                 3   0.00580 DNA dama…
##  7 query_1 TRUE        0.0107          517                 5   0.00967 intrinsi…
##  8 query_1 TRUE        0.0184          517                 6   0.0116  negative…
##  9 query_1 TRUE        0.0269          517                 4   0.00774 regulati…
## 10 query_1 TRUE        0.0304          517                 9   0.0174  signal t…

Intersection with ChP dynamicaly expressed genes

ChP_dynamic_genes <- read.table("../ChoroidPlexus_trajectory/ChP.Gene.dynamique.csv", sep = ";", header = T, row.names = 1)
CR_ChP_common_genes <- CR.Gene.dynamique %>% filter(Gene %in% ChP_dynamic_genes$Gene)
gene.order2 <- gene.order[gene.order %in% CR_ChP_common_genes$Gene]

pheatmap::pheatmap(CR_curve_matrix[gene.order2,],
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   #annotation_row = CR.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   #annotation_col = col.anno,
                   #annotation_colors = anno.colors,
                   gaps_col = 70,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

CR_ChP_common.gostres <- gost(query = list("Clust.1" = CR_ChP_common_genes %>% filter(Gene.Clusters == "Clust.1") %>% pull(Gene) %>% as.character(),
                             "Clust.2" = CR.Gene.dynamique %>% filter(Gene.Clusters == "Clust.2") %>% pull(Gene) %>% as.character(),
                             "Clust.3" = CR.Gene.dynamique %>% filter(Gene.Clusters == "Clust.3") %>% pull(Gene) %>% as.character(),
                             "Clust.4" = CR.Gene.dynamique %>% filter(Gene.Clusters == "Clust.4") %>% pull(Gene) %>% as.character(),
                             "Clust.5" = CR.Gene.dynamique %>% filter(Gene.Clusters == "Clust.5") %>% pull(Gene) %>% as.character()),
                organism = "mmusculus", ordered_query = F, 
                multi_query = F, significant = T, exclude_iea = T, 
                measure_underrepresentation = F, evcodes = T, 
                user_threshold = 0.05, correction_method = "fdr", 
                domain_scope = "annotated", custom_bg = NULL, 
                numeric_ns = "", sources = c("GO:MF", "GO:BP"), as_short_link = F)

write.table(apply(CR_ChP_common.gostres$result,2,as.character),
            "CR_ChP_common_GO_res-by-waves.csv", sep = ";", quote = F, row.names = F)
CR_ChP_common.gostres <- gost(query = as.character(CR_ChP_common_genes$Gene),
                organism = "mmusculus", ordered_query = F, 
                multi_query = F, significant = T, exclude_iea = T, 
                measure_underrepresentation = F, evcodes = T, 
                user_threshold = 0.05, correction_method = "fdr", 
                domain_scope = "annotated", custom_bg = NULL, 
                numeric_ns = "", sources = c("GO:MF", "GO:BP"), as_short_link = F)

write.table(apply(CR_ChP_common.gostres$result,2,as.character),
            "CR_ChP_common_GO_res_all.csv", sep = ";", quote = F, row.names = F)

Pallial neurons trajectory analysis

# Extract Pallial neurons trajectory genes
Pal.res <- as.data.frame(pseudo.maturation.diff.filtered[pseudo.maturation.diff.filtered$ABCs < 0,])
Pal.genes <- row.names(Pal.res)

Pal_curve_matrix <- Pal_curve_matrix[Pal.genes, ]

Gene expression profiles along the trajectory

## Cluster gene by expression profiles
Pseudotime.genes.clusters <- cluster::pam(as.dist((1 - cor(Matrix::t(Pal_curve_matrix),method = "pearson"))), k= 5)

Pal.Gene.dynamique <- data.frame(Gene= names(Pseudotime.genes.clusters$clustering),
                             Waves= Pseudotime.genes.clusters$clustering,
                             Gene.Clusters = Pseudotime.genes.clusters$clustering,
                             q.val = Pal.res$pval,
                             ABCs= Pal.res$ABCs
                             ) %>% arrange(Gene.Clusters)

row.names(Pal.Gene.dynamique) <- Pal.Gene.dynamique$Gene
Pal.Gene.dynamique$Gene.Clusters <- paste0("Clust.", Pal.Gene.dynamique$Gene.Clusters)
# Order the rows using seriation
dst <- as.dist((1-cor(scale(t(Pal_curve_matrix)), method = "pearson")))
row.ser <- seriation::seriate(dst, method ="R2E") #"R2E" #TSP #"GW" "GW_ward"
gene.order <- rownames(Pal_curve_matrix[get_order(row.ser),])

# Set annotation colors
pal <- wes_palette("Darjeeling1")
anno.colors <- list(lineage = c(Pallial_neurons="#026c9a", Cajal_Retzius="#cc391b"),
                    Gene.Clusters = c(Clust.1 =pal[1] , Clust.2=pal[2], Clust.3=pal[3], Clust.4=pal[4], Clust.5=pal[5]))


pheatmap::pheatmap(Diff.curve_matrix[gene.order,
                                c(200:1,#Pal
                                  201:400)], #CR
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = Pal.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_col = data.frame(lineage = rep(c("Pallial_neurons","Cajal_Retzius"), each=200)),
                   annotation_colors = anno.colors,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

We manually correct the reordering so genes are aligned from top right to bottom left

gene.order <- gene.order[c(199:1,352:200)]

pheatmap::pheatmap(Diff.curve_matrix[gene.order,
                                c(200:1,#Pal
                                  201:400)], #CR
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = Pal.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_col = data.frame(lineage = rep(c("Pallial_neurons","Cajal_Retzius"), each=200)),
                   annotation_colors = anno.colors,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

anno.colors <- list(Cell.state = c(Cycling_RG="#046c9a", Differentiating_cells="#ebcb2e"),
                    Gene.Clusters = c(Clust.1 =pal[1] , Clust.2=pal[2], Clust.3=pal[3], Clust.4=pal[4], Clust.5=pal[5]))

col.anno <- data.frame(Cell.state = rep(c("Differentiating_cells","Cycling_RG"), c(130,70)))
rownames(col.anno) <- 200:1

pheatmap::pheatmap(Pal_curve_matrix[gene.order,200:1],
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = Pal.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_col = col.anno,
                   annotation_colors = anno.colors,
                   gaps_col = 130,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

Gene cluster trend

Plot.clust.trends(Neuro.trajectories,
                   Lineage = "Pallial_neurons",
                   Which.cluster = 1:5,
                   clust.list = Pseudotime.genes.clusters$clustering,
                   Smooth.method = "gam")

Pal.gostres <- gost(query = as.character(Pal.Gene.dynamique$Gene),
                organism = "mmusculus", ordered_query = F, 
                multi_query = F, significant = T, exclude_iea = T, 
                measure_underrepresentation = F, evcodes = T, 
                user_threshold = 0.05, correction_method = "fdr", 
                domain_scope = "annotated", custom_bg = NULL, 
                numeric_ns = "", sources = c("GO:MF", "GO:BP"), as_short_link = F)

write.table(apply(Pal.gostres$result, 2, as.character),
            "Pal.gostrescsv", sep = ";", quote = F, row.names = F)
DNA_damage_GOterm <- Pal.gostres$result[Pal.gostres$result$term_id %in% c("GO:0008630", "GO:0030330", "GO:0031571", "GO:0006974", "GO:0006977",
                                                                                 "GO:0044773", "GO:0042771", "GO:0042770", "GO:2001021", "GO:1902229"),]

DNA_damage_GOterm[,c(1,2,3,5,6,7,11)]
## # A tibble: 6 × 7
##   query   significant p_value query_size intersection_size precision term_name  
##   <chr>   <lgl>         <dbl>      <int>             <int>     <dbl> <chr>      
## 1 query_1 TRUE        0.00239        300                 9    0.03   signal tra…
## 2 query_1 TRUE        0.00872        300                 4    0.0133 regulation…
## 3 query_1 TRUE        0.0151         300                 5    0.0167 negative r…
## 4 query_1 TRUE        0.0191         300                 6    0.02   intrinsic …
## 5 query_1 TRUE        0.0196         300                 3    0.01   mitotic G1…
## 6 query_1 TRUE        0.0395         300                 4    0.0133 DNA damage…

Session Info

#date
format(Sys.time(), "%d %B, %Y, %H,%M")
## [1] "12 avril, 2022, 16,11"
#Packages used
sessionInfo()
## R version 4.1.3 (2022-03-10)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.4 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0
## 
## locale:
##  [1] LC_CTYPE=fr_FR.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=fr_FR.UTF-8        LC_COLLATE=fr_FR.UTF-8    
##  [5] LC_MONETARY=fr_FR.UTF-8    LC_MESSAGES=fr_FR.UTF-8   
##  [7] LC_PAPER=fr_FR.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=fr_FR.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] splines   stats4    stats     graphics  grDevices utils     datasets 
## [8] methods   base     
## 
## other attached packages:
##  [1] wesanderson_0.3.6   cowplot_1.1.1       ggExtra_0.9        
##  [4] RColorBrewer_1.1-2  dplyr_1.0.7         seriation_1.3.1    
##  [7] gprofiler2_0.2.1    monocle_2.22.0      DDRTree_0.1.5      
## [10] irlba_2.3.3         VGAM_1.1-5          ggplot2_3.3.5      
## [13] Biobase_2.54.0      BiocGenerics_0.40.0 Matrix_1.4-1       
## [16] princurve_2.1.6     SeuratObject_4.0.4  Seurat_4.0.5       
## 
## loaded via a namespace (and not attached):
##   [1] plyr_1.8.6            igraph_1.2.11         lazyeval_0.2.2       
##   [4] densityClust_0.3      listenv_0.8.0         scattermore_0.7      
##   [7] fastICA_1.2-3         digest_0.6.29         foreach_1.5.1        
##  [10] htmltools_0.5.2       viridis_0.6.2         fansi_0.5.0          
##  [13] magrittr_2.0.2        tensor_1.5            cluster_2.1.3        
##  [16] ROCR_1.0-11           limma_3.50.0          globals_0.14.0       
##  [19] matrixStats_0.61.0    docopt_0.7.1          spatstat.sparse_2.0-0
##  [22] colorspace_2.0-2      ggrepel_0.9.1         xfun_0.28            
##  [25] RCurl_1.98-1.5        sparsesvd_0.2         crayon_1.4.2         
##  [28] jsonlite_1.7.2        spatstat.data_2.1-0   survival_3.2-13      
##  [31] zoo_1.8-9             iterators_1.0.13      glue_1.5.1           
##  [34] polyclip_1.10-0       registry_0.5-1        gtable_0.3.0         
##  [37] leiden_0.3.9          future.apply_1.8.1    abind_1.4-5          
##  [40] scales_1.1.1          pheatmap_1.0.12       DBI_1.1.1            
##  [43] miniUI_0.1.1.1        Rcpp_1.0.8            viridisLite_0.4.0    
##  [46] xtable_1.8-4          reticulate_1.22       spatstat.core_2.3-1  
##  [49] htmlwidgets_1.5.4     httr_1.4.2            FNN_1.1.3            
##  [52] ellipsis_0.3.2        ica_1.0-2             farver_2.1.0         
##  [55] pkgconfig_2.0.3       sass_0.4.0            uwot_0.1.10          
##  [58] deldir_1.0-6          utf8_1.2.2            labeling_0.4.2       
##  [61] tidyselect_1.1.1      rlang_0.4.12          reshape2_1.4.4       
##  [64] later_1.3.0           munsell_0.5.0         tools_4.1.3          
##  [67] cli_3.1.0             generics_0.1.1        ggridges_0.5.3       
##  [70] evaluate_0.14         stringr_1.4.0         fastmap_1.1.0        
##  [73] yaml_2.2.1            goftest_1.2-3         knitr_1.36           
##  [76] fitdistrplus_1.1-6    purrr_0.3.4           RANN_2.6.1           
##  [79] pbapply_1.5-0         future_1.23.0         nlme_3.1-153         
##  [82] mime_0.12             slam_0.1-49           rstudioapi_0.13      
##  [85] compiler_4.1.3        plotly_4.10.0         png_0.1-7            
##  [88] spatstat.utils_2.2-0  tibble_3.1.6          bslib_0.3.1          
##  [91] stringi_1.7.6         highr_0.9             lattice_0.20-45      
##  [94] HSMMSingleCell_1.14.0 vctrs_0.3.8           pillar_1.6.4         
##  [97] lifecycle_1.0.1       spatstat.geom_2.3-0   combinat_0.0-8       
## [100] lmtest_0.9-39         jquerylib_0.1.4       RcppAnnoy_0.0.19     
## [103] bitops_1.0-7          data.table_1.14.2     httpuv_1.6.3         
## [106] patchwork_1.1.1       R6_2.5.1              promises_1.2.0.1     
## [109] TSP_1.1-11            KernSmooth_2.23-20    gridExtra_2.3        
## [112] parallelly_1.29.0     codetools_0.2-18      MASS_7.3-56          
## [115] assertthat_0.2.1      withr_2.4.3           qlcMatrix_0.9.7      
## [118] sctransform_0.3.2     mgcv_1.8-40           parallel_4.1.3       
## [121] grid_4.1.3            rpart_4.1.16          tidyr_1.1.4          
## [124] rmarkdown_2.11        Rtsne_0.15            shiny_1.7.1

  1. Institute of Psychiatry and Neuroscience of Paris, INSERM U1266, 75014, Paris, France, ↩︎

LS0tCnRpdGxlOiAiQ2FqYWwtUmV0eml1cyBjZWxscyBUcmFqZWN0b3J5IgphdXRob3I6CiAgIC0gTWF0dGhpZXUgTW9yZWF1XltJbnN0aXR1dGUgb2YgUHN5Y2hpYXRyeSBhbmQgTmV1cm9zY2llbmNlIG9mIFBhcmlzLCBJTlNFUk0gVTEyNjYsIDc1MDE0LCBQYXJpcywgRnJhbmNlLCBtYXR0aGlldS5tb3JlYXVAaW5zZXJtLmZyXSBbIVtdKGh0dHBzOi8vb3JjaWQub3JnL3NpdGVzL2RlZmF1bHQvZmlsZXMvaW1hZ2VzL29yY2lkXzE2eDE2LnBuZyldKGh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMi0yNTkyLTIzNzMpCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OiAKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgZGZfcHJpbnQ6IHRpYmJsZQogICAgaGlnaGxpZ2h0OiBoYWRkb2NrCiAgICB0aGVtZTogY29zbW8KICAgIGNzczogIi4uL3N0eWxlLmNzcyIKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBmaWcuYWxpZ24gPSAnY2VudGVyJywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGUubGF6eSA9IEZBTFNFKQoKIyBUbyB1c2UgYmlvbWFydCAKbmV3X2NvbmZpZyA8LSBodHRyOjpjb25maWcoc3NsX3ZlcmlmeXBlZXIgPSBGQUxTRSkKaHR0cjo6c2V0X2NvbmZpZyhuZXdfY29uZmlnLCBvdmVycmlkZSA9IEZBTFNFKQpgYGAKCiMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KHByaW5jdXJ2ZSkKbGlicmFyeShtb25vY2xlKQpsaWJyYXJ5KGdwcm9maWxlcjIpCmxpYnJhcnkoc2VyaWF0aW9uKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShkcGx5cikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ0V4dHJhKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkod2VzYW5kZXJzb24pCgojU2V0IGdncGxvdCB0aGVtZSBhcyBjbGFzc2ljCnRoZW1lX3NldCh0aGVtZV9jbGFzc2ljKCkpCmBgYAoKIyBMb2FkIHRoZSBmdWxsIGRhdGFzZXQKCmBgYHtyfQpIZW0uZGF0YSA8LSByZWFkUkRTKCIuLi9RQy5maWx0ZXJlZC5jbHVzdGVyZWQuY2VsbHMuUkRTIikKYGBgCgpgYGB7cn0KRGltUGxvdChvYmplY3QgPSBIZW0uZGF0YSwKICAgICAgICBncm91cC5ieSA9ICJDZWxsX2lkZW50IiwKICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICBjb2xzID0gYygiI2ViY2IyZSIsICMiQ1IiCiAgICAgICAgICAgICIjZTc4MjNhIiwgIyJDaFAiCiAgICAgICAgICAgICIjNGNhYmRjIiwgIyBDaHBfcHJvZwogICAgICAgICAgICAiIzY4YjA0MSIsICMiRG9yc28tTWVkaWFsX3BhbGxpdW0iIAogICAgICAgICAgICAiI2U0NmI2YiIsICMiSGVtIiAKICAgICAgICAgICAgIiNlM2MxNDgiLCAjIk1lZGlhbF9wYWxsaXVtIgogICAgICAgICAgICAiIzA0NmM5YSIsICMgUGFsbGlhbAogICAgICAgICAgICAiIzQ5OTBjOSIjIlRoYWxhbWljX2VtaW5lbmNlIgogICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQpgYGAKCmBgYHtyfQpIZW0uZGF0YSRMaW5lYWdlIDwtIHNhcHBseShIZW0uZGF0YSRDZWxsX2lkZW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHggJWluJSBjKCJDYWphbC1SZXR6aXVzX25ldXJvbnMiLCAiSGVtIikpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAiQ2FqYWwtUmV0eml1c19uZXVyb25zIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoeCAlaW4lIGMoIlBhbGxpYWxfbmV1cm9ucyIsICJNZWRpYWxfcGFsbGl1bSIpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gIlBhbGxpYWxfbmV1cm9ucyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYoeCAlaW4lIGMoIkNoUCIsICJDaFBfcHJvZ2VuaXRvcnMiKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAgIkNob3JvaWRfUGxleHVzIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gIm90aGVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KQpgYGAKCmBgYHtyfQpEaW1QbG90KEhlbS5kYXRhLAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIGdyb3VwLmJ5ID0gIkxpbmVhZ2UiLAogICAgICAgIHB0LnNpemUgPSAxLAogICAgICAgIGNvbHMgPSAgYygiI2NjMzkxYiIsIiNlNzgyM2EiLCIjOTY5Njk2IiwiIzAyNmM5YSIpCiAgICAgICAgKSArIE5vQXhlcygpCmBgYAoKIyBEaWZmZXJlbnRpYXRpbmcgbmV1cm9ucyB0cmFqZWN0b3J5CgpgYGB7cn0KTmV1cm9ucy5kYXRhIDwtICBzdWJzZXQoSGVtLmRhdGEsIGlkZW50cyA9IGMoIkNhamFsLVJldHppdXNfbmV1cm9ucyIsICJQYWxsaWFsX25ldXJvbnMiKSkKCkRpbVBsb3QoTmV1cm9ucy5kYXRhICwKICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICBwdC5zaXplID0gMSwKICAgICAgICBjb2xzID0gIGMoIiNjYzM5MWIiLCIjMDI2YzlhIikKICAgICAgICApICsgTm9BeGVzKCkKYGBgCgojIyBGaXQgcHJpbmNpcGFsZSBjdXJ2ZSBvbiB0aGUgdHdvIGxpbmVhZ2VzCgojIyMgQ2FqYWwtUmV0eml1cyBjZWxscwoKYGBge3J9ClRyYWplY3Rvcmllcy5IZW0gPC0gTmV1cm9ucy5kYXRhQG1ldGEuZGF0YSAlPiUKICAgICAgICAgICAgICAgICAgICBzZWxlY3QoIkJhcmNvZGVzIiwgIm5VTUkiLCAiU3ByaW5nXzEiLCAiU3ByaW5nXzIiLCAiQVBfc2lnbmF0dXJlMSIsIkJQX3NpZ25hdHVyZTEiLCAiRU5fc2lnbmF0dXJlMSIsICJMTl9zaWduYXR1cmUxIiwgIkxpbmVhZ2UiKSAlPiUKICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoTGluZWFnZSA9PSAiQ2FqYWwtUmV0eml1c19uZXVyb25zIikKYGBgCgpgYGB7cn0KZml0IDwtIHByaW5jaXBhbF9jdXJ2ZShhcy5tYXRyaXgoVHJhamVjdG9yaWVzLkhlbVssYygiU3ByaW5nXzEiLCAiU3ByaW5nXzIiKV0pLAogICAgICAgICAgICAgICAgICAgICAgIHNtb290aGVyPSdsb3dlc3MnLAogICAgICAgICAgICAgICAgICAgICAgIHRyYWNlPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgZiA9IC43LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmV0Y2g9MCkKCiNUaGUgcHJpbmNpcGFsIGN1cnZlIHNtb290aGVkCkhlbS5wYy5saW5lIDwtIGFzLmRhdGEuZnJhbWUoZml0JHNbb3JkZXIoZml0JGxhbWJkYSksXSkgCgojUHNldWRvdGltZSBzY29yZQpUcmFqZWN0b3JpZXMuSGVtJFBzZXVkb3RpbWVTY29yZSA8LSBmaXQkbGFtYmRhL21heChmaXQkbGFtYmRhKQoKYGBgCgpgYGB7cn0KaWYgKGNvcihUcmFqZWN0b3JpZXMuSGVtJFBzZXVkb3RpbWVTY29yZSwgTmV1cm9ucy5kYXRhQGFzc2F5cyRTQ1RAZGF0YVsnSG1nYTInLCBUcmFqZWN0b3JpZXMuSGVtJEJhcmNvZGVzXSkgPiAwKSB7CiAgVHJhamVjdG9yaWVzLkhlbSRQc2V1ZG90aW1lU2NvcmUgPC0gLShUcmFqZWN0b3JpZXMuSGVtJFBzZXVkb3RpbWVTY29yZSAtIG1heChUcmFqZWN0b3JpZXMuSGVtJFBzZXVkb3RpbWVTY29yZSkpCn0KYGBgCgojIyMgUGFsbGlhbCBuZXVyb25zCgpgYGB7cn0KVHJhamVjdG9yaWVzLlBhbGxpYWwgPC0gTmV1cm9ucy5kYXRhQG1ldGEuZGF0YSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KCJCYXJjb2RlcyIsICJuVU1JIiwgIlNwcmluZ18xIiwgIlNwcmluZ18yIiwgIkFQX3NpZ25hdHVyZTEiLCJCUF9zaWduYXR1cmUxIiwgIkVOX3NpZ25hdHVyZTEiLCAiTE5fc2lnbmF0dXJlMSIsICJMaW5lYWdlIikgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihMaW5lYWdlID09ICJQYWxsaWFsX25ldXJvbnMiKQogICAgICAgICAgICAgICAgICAKYGBgCgpgYGB7cn0KZml0IDwtIHByaW5jaXBhbF9jdXJ2ZShhcy5tYXRyaXgoVHJhamVjdG9yaWVzLlBhbGxpYWxbLGMoIlNwcmluZ18xIiwgIlNwcmluZ18yIildKSwKICAgICAgICAgICAgICAgICAgICAgICBzbW9vdGhlcj0nbG93ZXNzJywKICAgICAgICAgICAgICAgICAgICAgICB0cmFjZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgIGYgPSAuNywKICAgICAgICAgICAgICAgICAgICAgICBzdHJldGNoPTApCgojVGhlIHByaW5jaXBhbCBjdXJ2ZSBzbW9vdGhlZApQYWxsaWFsLnBjLmxpbmUgPC0gYXMuZGF0YS5mcmFtZShmaXQkc1tvcmRlcihmaXQkbGFtYmRhKSxdKQoKI1BzZXVkb3RpbWUgc2NvcmUKVHJhamVjdG9yaWVzLlBhbGxpYWwkUHNldWRvdGltZVNjb3JlIDwtIGZpdCRsYW1iZGEvbWF4KGZpdCRsYW1iZGEpCmBgYAoKYGBge3J9CmlmIChjb3IoVHJhamVjdG9yaWVzLlBhbGxpYWwkUHNldWRvdGltZVNjb3JlLCBOZXVyb25zLmRhdGFAYXNzYXlzJFNDVEBkYXRhWydIbWdhMicsIFRyYWplY3Rvcmllcy5QYWxsaWFsJEJhcmNvZGVzXSkgPiAwKSB7CiAgVHJhamVjdG9yaWVzLlBhbGxpYWwkUHNldWRvdGltZVNjb3JlIDwtIC0oVHJhamVjdG9yaWVzLlBhbGxpYWwkUHNldWRvdGltZVNjb3JlIC0gbWF4KFRyYWplY3Rvcmllcy5QYWxsaWFsJFBzZXVkb3RpbWVTY29yZSkpCn0KYGBgCgojIyBDb21iaW5lIHRoZSB0d28gdHJhamVjdG9yaWVzJyBkYXRhCgpgYGB7cn0KVHJhamVjdG9yaWVzLm5ldXJvbnMgPC0gcmJpbmQoVHJhamVjdG9yaWVzLlBhbGxpYWwsIFRyYWplY3Rvcmllcy5IZW0pCmBgYAoKYGBge3J9CmNvbHMgPC0gYnJld2VyLnBhbChuID0xMSwgbmFtZSA9ICJTcGVjdHJhbCIpCgpnZ3Bsb3QoVHJhamVjdG9yaWVzLm5ldXJvbnMsIGFlcyhTcHJpbmdfMSwgU3ByaW5nXzIpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9UHNldWRvdGltZVNjb3JlKSwgc2l6ZT0yLCBzaGFwZT0xNikgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3Vycz1yZXYoY29scyksIG5hbWU9J1NwZXVkb3RpbWUgc2NvcmUnKSArCiAgZ2VvbV9saW5lKGRhdGE9UGFsbGlhbC5wYy5saW5lLCBjb2xvcj0iIzAyNmM5YSIsIHNpemU9MC43NykgKwogIGdlb21fbGluZShkYXRhPUhlbS5wYy5saW5lLCBjb2xvcj0iI2NjMzkxYiIsIHNpemU9MC43NykKYGBgCgojIyBQbG90IHBhbi1uZXVyb25hbCBnZW5lcyBhbG9uZyB0aGlzIGF4aXMKCmBgYHtyfQpOZXVyb25zLmRhdGEgPC0gTm9ybWFsaXplRGF0YShOZXVyb25zLmRhdGEsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIHNjYWxlLmZhY3RvciA9IDEwMDAwLCBhc3NheSA9ICJSTkEiKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg5LDEwKX0KIyBOZXVyb2cyCnAxIDwtIEZlYXR1cmVQbG90KG9iamVjdCA9IE5ldXJvbnMuZGF0YSwKICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJOZXVyb2cyIiksCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCBicmV3ZXIucGFsKDksIllsR25CdSIpKSwKICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgIG9yZGVyID0gVCkgJiBOb0F4ZXMoKQoKVHJhamVjdG9yaWVzLm5ldXJvbnMkTmV1cm9nMiA8LSBOZXVyb25zLmRhdGFAYXNzYXlzJFJOQUBkYXRhWyJOZXVyb2cyIiwgVHJhamVjdG9yaWVzLm5ldXJvbnMkQmFyY29kZXNdCgpwMiA8LSBnZ3Bsb3QoVHJhamVjdG9yaWVzLm5ldXJvbnMsIGFlcyh4PSBQc2V1ZG90aW1lU2NvcmUsIHk9IE5ldXJvZzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IExpbmVhZ2UpLCBzaXplPTAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKSArCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDUwLCBhZXMoY29sb3I9IExpbmVhZ2UpKSArCiAgICAgICAgeWxpbSgwLE5BKQoKIyBUYnIxIApwMyA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBOZXVyb25zLmRhdGEgLAogICAgICAgICAgICBmZWF0dXJlcyA9IGMoIlRicjEiKSwKICAgICAgICAgICAgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgY29scyA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICAgICAgb3JkZXIgPSBUKSAmIE5vQXhlcygpClRyYWplY3Rvcmllcy5uZXVyb25zJFRicjEgPC0gTmV1cm9ucy5kYXRhQGFzc2F5cyRSTkFAZGF0YVsiVGJyMSIsIFRyYWplY3Rvcmllcy5uZXVyb25zJEJhcmNvZGVzXQoKcDQgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5uZXVyb25zLCBhZXMoeD0gUHNldWRvdGltZVNjb3JlLCB5PSBUYnIxKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkgKwogICAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgYWVzKGNvbG9yPSBMaW5lYWdlKSkgKwogICAgICAgIHlsaW0oMCxOQSkKCiMgTWFwdCAKcDUgPC0gRmVhdHVyZVBsb3Qob2JqZWN0ID0gTmV1cm9ucy5kYXRhICwKICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJNYXB0IiksCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCBicmV3ZXIucGFsKDksIllsR25CdSIpKSwKICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgIG9yZGVyID0gVCkgJiBOb0F4ZXMoKQoKVHJhamVjdG9yaWVzLm5ldXJvbnMkTWFwdCA8LSBOZXVyb25zLmRhdGFAYXNzYXlzJFJOQUBkYXRhWyJNYXB0IiwgVHJhamVjdG9yaWVzLm5ldXJvbnMkQmFyY29kZXNdCgpwNiA8LSBnZ3Bsb3QoVHJhamVjdG9yaWVzLm5ldXJvbnMsIGFlcyh4PSBQc2V1ZG90aW1lU2NvcmUsIHk9IE1hcHQpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IExpbmVhZ2UpLCBzaXplPTAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKSArCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDUwLCBhZXMoY29sb3I9IExpbmVhZ2UpKSArCiAgICAgICAgeWxpbSgwLE5BKQoKcDEgKyBwMiArIHAzICsgcDQgKyBwNSArIHA2ICsgcGF0Y2h3b3JrOjpwbG90X2xheW91dChuY29sID0gMikKYGBgCgojIyBTaGlmdCBQc2V1ZG90aW1lIGluIGJvdGggbGluZWFnZQoKU2luY2Ugd2Ugb2JzZXJ2ZSB0aGUgZmlyc3QgMjUlIG9mIGJvdGggdHJhamVjdG9yaWVzIGFyZSBvY2N1cGllZCBieSBmZXcsIGxpa2VseSBwcm9nZW5pdG9yIGNlbGxzLCB3ZSBzaGlmdCB0aGlzIGNlbGwgYWxvbmcgdGhlIGF4aXMKCmBgYHtyfQpQc2V1ZG90aW1lLmludGVydmFscyA8LSBUcmFqZWN0b3JpZXMubmV1cm9ucyU+JQogICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChMaW5lYWdlLCBQc2V1ZG90aW1lU2NvcmUpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShQc2V1ZG90aW1lLmJpbnMgPSBjdXQoVHJhamVjdG9yaWVzLm5ldXJvbnMkUHNldWRvdGltZVNjb3JlLCBzZXEoMCwgbWF4KFRyYWplY3Rvcmllcy5uZXVyb25zJFBzZXVkb3RpbWVTY29yZSkgKyAwLjA1LCAwLjA1KSwgZGlnLmxhYiA9IDIsIHJpZ2h0ID0gRkFMU0UpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieShMaW5lYWdlLCBQc2V1ZG90aW1lLmJpbnMpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShuPW4oKSkKCmdncGxvdChQc2V1ZG90aW1lLmludGVydmFscywgYWVzKHg9UHNldWRvdGltZS5iaW5zLCB5PW4sIGZpbGw9TGluZWFnZSkpICsKICAgICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjkwKSArCiAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3Q9MSkpKwogICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0gYygiI2NjMzkxYiIsICIjMDI2YzlhIikpCmBgYAoKYGBge3J9CnNjb3JlIDwtIHNhcHBseShUcmFqZWN0b3JpZXMubmV1cm9ucyRQc2V1ZG90aW1lU2NvcmUsCiAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4KSBpZiAoeCA8PSAwLjIpIHt4PSAwLjJ9IGVsc2UgeyB4PXggfSkKClRyYWplY3Rvcmllcy5uZXVyb25zJFBzZXVkb3RpbWVTY29yZS5zaGlmdGVkIDwtIChzY29yZSAtIG1pbihzY29yZSkpIC8gKG1heChzY29yZSkgLSBtaW4oc2NvcmUpKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg5LDEwKX0KIyBOZXVyb2cyCnAxIDwtIEZlYXR1cmVQbG90KG9iamVjdCA9IE5ldXJvbnMuZGF0YSAsCiAgICAgICAgICAgIGZlYXR1cmVzID0gYygiTmV1cm9nMiIpLAogICAgICAgICAgICBwdC5zaXplID0gMC41LAogICAgICAgICAgICBjb2xzID0gYygiZ3JleTkwIiwgYnJld2VyLnBhbCg5LCJZbEduQnUiKSksCiAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgICAgICBvcmRlciA9IFQpICYgTm9BeGVzKCkKCnAyIDwtIGdncGxvdChUcmFqZWN0b3JpZXMubmV1cm9ucywgYWVzKHg9IFBzZXVkb3RpbWVTY29yZS5zaGlmdGVkLCB5PSBOZXVyb2cyKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkgKwogICAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgYWVzKGNvbG9yPSBMaW5lYWdlKSkgKwogICAgICAgIHlsaW0oMCxOQSkKCiMgVGJyMSAKcDMgPC0gRmVhdHVyZVBsb3Qob2JqZWN0ID0gTmV1cm9ucy5kYXRhICwKICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJUYnIxIiksCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCBicmV3ZXIucGFsKDksIllsR25CdSIpKSwKICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgIG9yZGVyID0gVCkgJiBOb0F4ZXMoKQoKcDQgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5uZXVyb25zLCBhZXMoeD0gUHNldWRvdGltZVNjb3JlLnNoaWZ0ZWQsIHk9IFRicjEpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IExpbmVhZ2UpLCBzaXplPTAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKSArCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDUwLCBhZXMoY29sb3I9IExpbmVhZ2UpKSArCiAgICAgICAgeWxpbSgwLE5BKQoKIyBNYXB0IApwNSA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBOZXVyb25zLmRhdGEgLAogICAgICAgICAgICBmZWF0dXJlcyA9IGMoIk1hcHQiKSwKICAgICAgICAgICAgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgY29scyA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICAgICAgb3JkZXIgPSBUKSAmIE5vQXhlcygpCgpwNiA8LSBnZ3Bsb3QoVHJhamVjdG9yaWVzLm5ldXJvbnMsIGFlcyh4PSBQc2V1ZG90aW1lU2NvcmUuc2hpZnRlZCwgeT0gTWFwdCkpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gTGluZWFnZSksIHNpemU9MC41KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiI2NjMzkxYiIsICIjMDI2YzlhIikpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgbj0gNTAsIGFlcyhjb2xvcj0gTGluZWFnZSkpICsKICAgICAgICB5bGltKDAsTkEpCgpwMSArIHAyICsgcDMgKyBwNCArIHA1ICsgcDYgKyBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoVHJhamVjdG9yaWVzLm5ldXJvbnMsIGFlcyh4PSBQc2V1ZG90aW1lU2NvcmUuc2hpZnRlZCwgeT0gblVNSS8xMDAwMCkpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gTGluZWFnZSksIHNpemU9MC41KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiI2NjMzkxYiIsICIjMDI2YzlhIikpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgbj0gNTAsIGFlcyhjb2xvcj0gTGluZWFnZSkpICsKICAgICAgICB5bGltKDAsTkEpCmBgYAoKYGBge3J9CnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgYygiVHJhamVjdG9yaWVzLm5ldXJvbnMiKV0pCmBgYAoKIyBMb2FkIHByb2dlbml0b3JzIHdpdGggY2VsbCBjeWNsZSB0cmFqZWN0b3J5IGZpdHRlZAoKYGBge3J9ClByb2dlbml0b3JzLmRhdGEgPC0gcmVhZFJEUygiLi4vUHJvZ2VuaXRvcnNEaXZlcnNpdHkvUHJvZ2VuaXRvcnMuUkRTIikKYGBgCgpgYGB7cn0KdGFibGUoUHJvZ2VuaXRvcnMuZGF0YSRDZWxsX2lkZW50KQpgYGAKClRvIGJhbGFuY2UgdGhlIG51bWJlciBvZiBwcm9nZW5pdG9ycyBpbiBib3RoIGRvbWFpbiB3ZSB3aWxsIG9ubHkgd29yayB3aXRoICpIZW0qIGFuZCAqTWVkaWFsX3BhbGxpdW0qIGFubm90YXRlZCBjZWxscy4gU2luY2Ugd2UgYXJlIHVzaW5nIHBhbGxpYWwgY2VsbCB0byBjb250cmFzdCBDUiBzcGVjaWZpYyB0cmFqZWN0b3J5IHdlIHRoaW5rIHRoaXMgYXBwcm94aW1hdGlvbiB3aWxsIG5vdCBzaWduaWZpY2FudGx5IGFmZmVjdCBvdXIgYW5hbHlzaXMuCgpgYGB7cn0KUHJvZ2VuaXRvcnMuZGF0YSA8LSAgc3Vic2V0KFByb2dlbml0b3JzLmRhdGEsIGlkZW50cyA9IGMoIkhlbSIsICJNZWRpYWxfcGFsbGl1bSIpKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg2LCA0KX0KcDEgPC0gRGltUGxvdChQcm9nZW5pdG9ycy5kYXRhLAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgY29scyA9ICBjKCIjZTNjMTQ4IiwgIiNlNDZiNmIiKSkgKyBOb0F4ZXMoKQoKcDIgPC0gRmVhdHVyZVBsb3Qob2JqZWN0ID0gUHJvZ2VuaXRvcnMuZGF0YSwKICAgICAgICAgICAgZmVhdHVyZXMgPSAiUmV2ZWxpby5jYyIsCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgIGNvbHMgPSByZXYoYnJld2VyLnBhbCgxMCwiU3BlY3RyYWwiKSksCiAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgICAgICBvcmRlciA9IFQpICYgTm9BeGVzKCkKCnAzIDwtIERpbVBsb3Qob2JqZWN0ID0gUHJvZ2VuaXRvcnMuZGF0YSwKICAgICAgICBncm91cC5ieSA9ICJSZXZlbGlvLnBoYXNlIiwKICAgICAgICBwdC5zaXplID0gMC41LAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIGNvbHMgPSAgYyh3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzE6M10sImdyZXk0MCIsd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVs1XSkpICYgTm9BeGVzKCkKCnAxICsgcDIgKyBwMyAgKyBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCiMgQ29tYmluZWQgcHJvZ2VuaXRvcnMgYW5kIG5ldXJvbnMgYWxvbmcgUHNldWRvdGltZQoKYGBge3J9CiMgU3RhcnQgd2l0aCBuZXVyb25zIGRhdGEKVHJhamVjdG9yaWVzLmFsbCA8LSBUcmFqZWN0b3JpZXMubmV1cm9ucyAlPiUgc2VsZWN0KEJhcmNvZGVzLCBuVU1JLCBTcHJpbmdfMSwgU3ByaW5nXzIsIEFQX3NpZ25hdHVyZTEsIEJQX3NpZ25hdHVyZTEsIEVOX3NpZ25hdHVyZTEsIExOX3NpZ25hdHVyZTEsIExpbmVhZ2UpCgpUcmFqZWN0b3JpZXMuYWxsJFBzZXVkb3RpbWUgPC0gVHJhamVjdG9yaWVzLm5ldXJvbnMkUHNldWRvdGltZVNjb3JlLnNoaWZ0ZWQgKyAwLjUKVHJhamVjdG9yaWVzLmFsbCRQaGFzZSA8LSBOQQpgYGAKCmBgYHtyfQojIEFkZCBwcm9nZW5pdG9ycyBkYXRhClRyYWplY3Rvcmllcy5wcm9nZW5pdG9ycyA8LSBQcm9nZW5pdG9ycy5kYXRhQG1ldGEuZGF0YSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KEJhcmNvZGVzLCBuVU1JLCBTcHJpbmdfMSwgU3ByaW5nXzIsIEFQX3NpZ25hdHVyZTEsIEJQX3NpZ25hdHVyZTEsIEVOX3NpZ25hdHVyZTEsIExOX3NpZ25hdHVyZTEpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKExpbmVhZ2U9IGlmZWxzZShQcm9nZW5pdG9ycy5kYXRhJENlbGxfaWRlbnQgPT0gIk1lZGlhbF9wYWxsaXVtIiwgIlBhbGxpYWxfbmV1cm9ucyIsICJDYWphbC1SZXR6aXVzX25ldXJvbnMiKSAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQc2V1ZG90aW1lPSBQcm9nZW5pdG9ycy5kYXRhJFJldmVsaW8uY2MvMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBoYXNlID0gUHJvZ2VuaXRvcnMuZGF0YSRSZXZlbGlvLnBoYXNlKQpgYGAKCmBgYHtyfQpUcmFqZWN0b3JpZXMuYWxsIDwtIHJiaW5kKFRyYWplY3Rvcmllcy5hbGwsIFRyYWplY3Rvcmllcy5wcm9nZW5pdG9ycykKClRyYWplY3Rvcmllcy5hbGwkUGhhc2UgPC0gZmFjdG9yKFRyYWplY3Rvcmllcy5hbGwkUGhhc2UsIGxldmVscyA9IGMoIkcxLlMiLCAiUyIsICJHMiIsICJHMi5NIiwgIk0uRzEiKSkKYGBgCgpgYGB7ciBmaWcuZGltPWMoOSwzKX0KcDEgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5hbGwsIGFlcyhTcHJpbmdfMSwgU3ByaW5nXzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9UHNldWRvdGltZSksIHNpemU9MC41KSArIAogICAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzPXJldihicmV3ZXIucGFsKG4gPTExLCBuYW1lID0gIlNwZWN0cmFsIikpLCBuYW1lPSdTcGV1ZG90aW1lIHNjb3JlJykKCnAyIDwtIGdncGxvdChUcmFqZWN0b3JpZXMuYWxsLCBhZXMoU3ByaW5nXzEsIFNwcmluZ18yKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkKCnAxICsgcDIKYGBgCgpgYGB7ciBmaWcuZGltPWMoOSwzKX0KcDEgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5hbGwsIGFlcyh4PSBQc2V1ZG90aW1lLCB5PSBuVU1JLzEwMDAwKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBQaGFzZSksIHNpemU9MC41KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYyh3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzE6M10sImdyZXk0MCIsd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVs1XSkpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgbj0gNTAsIGZpbGw9ImdyZXkiKSArCiAgICAgICAgeWxpbSgwLE5BKQoKcDIgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5hbGwsIGFlcyh4PSBQc2V1ZG90aW1lLCB5PSBuVU1JLzEwMDAwKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkgKwogICAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgZmlsbD0iZ3JleSIpICsKICAgICAgICB5bGltKDAsTkEpCgpwMSAvIHAyCmBgYApgYGB7cn0KcDEgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5hbGwsIGFlcyh4PSBQc2V1ZG90aW1lLCB5PSBBUF9zaWduYXR1cmUxKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgZmlsbD0iZ3JleSIpCgoKcDIgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5hbGwsIGFlcyh4PSBQc2V1ZG90aW1lLCB5PSBCUF9zaWduYXR1cmUxKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgZmlsbD0iZ3JleSIpCgpwMyA8LSBnZ3Bsb3QoVHJhamVjdG9yaWVzLmFsbCwgYWVzKHg9IFBzZXVkb3RpbWUsIHk9IEVOX3NpZ25hdHVyZTEpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9IExpbmVhZ2UpLCBzaXplPTAuNSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDUwLCBmaWxsPSJncmV5IikKCnA0IDwtIGdncGxvdChUcmFqZWN0b3JpZXMuYWxsLCBhZXMoeD0gUHNldWRvdGltZSwgeT0gTE5fc2lnbmF0dXJlMSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gTGluZWFnZSksIHNpemU9MC41KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiI2NjMzkxYiIsICIjMDI2YzlhIikpICsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgbj0gNTAsIGZpbGw9ImdyZXkiKQoKCnAxIC8gcDIgLyBwMyAvIHA0CmBgYAoKCmBgYHtyfQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIGMoIlRyYWplY3Rvcmllcy5hbGwiKV0pCmBgYAoKIyBTdWJzZXQgdGhlIGZ1bGwgZGF0YXNldCBTZXVyYXQgb2JqZWN0CgpgYGB7cn0KSGVtLmRhdGEgPC0gcmVhZFJEUygiLi4vUUMuZmlsdGVyZWQuY2x1c3RlcmVkLmNlbGxzLlJEUyIpCmBgYAoKYGBge3J9Ck5ldXJvLnRyYWplY3RvcmllcyA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gSGVtLmRhdGFAYXNzYXlzJFJOQUBkYXRhWywgVHJhamVjdG9yaWVzLmFsbCRCYXJjb2Rlc10sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YS5kYXRhID0gVHJhamVjdG9yaWVzLmFsbCkKCnNwcmluZyA8LSBhcy5tYXRyaXgoTmV1cm8udHJhamVjdG9yaWVzQG1ldGEuZGF0YSAlPiUgc2VsZWN0KCJTcHJpbmdfMSIsICJTcHJpbmdfMiIpKQogIApOZXVyby50cmFqZWN0b3JpZXNbWyJzcHJpbmciXV0gPC0gQ3JlYXRlRGltUmVkdWNPYmplY3QoZW1iZWRkaW5ncyA9IHNwcmluZywga2V5ID0gIlNwcmluZ18iLCBhc3NheSA9IERlZmF1bHRBc3NheShOZXVyby50cmFqZWN0b3JpZXMpKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg2LCAxMil9CnAxIDwtIEZlYXR1cmVQbG90KG9iamVjdCA9IE5ldXJvLnRyYWplY3RvcmllcywKICAgICAgICAgICAgZmVhdHVyZXMgPSAiUHNldWRvdGltZSIsCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgIGNvbHMgPSByZXYoY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKG4gPTExLCBuYW1lID0gIlNwZWN0cmFsIikpKDEwMCkpLAogICAgICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICAgICAgb3JkZXIgPSBUKSAmIE5vQXhlcygpCgpwMiA8LSBEaW1QbG90KG9iamVjdCA9IE5ldXJvLnRyYWplY3RvcmllcywKICAgICAgICBncm91cC5ieSA9ICJMaW5lYWdlIiwKICAgICAgICBwdC5zaXplID0gMC41LAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIGNvbHMgPSAgYygiI2NjMzkxYiIsICIjMDI2YzlhIikpICYgTm9BeGVzKCkKCgpwMyA8LSBEaW1QbG90KG9iamVjdCA9IE5ldXJvLnRyYWplY3RvcmllcywKICAgICAgICBncm91cC5ieSA9ICJQaGFzZSIsCiAgICAgICAgcHQuc2l6ZSA9IDAuNSwKICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICBjb2xzID0gIGMod2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVsxOjNdLCJncmV5NDAiLHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbNV0pKSAmIE5vQXhlcygpCgpwMSArIHAyICsgcDMKYGBgCgoKYGBge3J9CnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgYygiTmV1cm8udHJhamVjdG9yaWVzIildKQpgYGAKCiMjIE5vcm1hbGl6YXRpb24KCmBgYHtyfQpOZXVyby50cmFqZWN0b3JpZXM8LSBOb3JtYWxpemVEYXRhKE5ldXJvLnRyYWplY3Rvcmllcywgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMTAwMDAsIGFzc2F5ID0gIlJOQSIpCmBgYAoKYGBge3J9Ck5ldXJvLnRyYWplY3RvcmllcyA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhOZXVyby50cmFqZWN0b3JpZXMsIHNlbGVjdGlvbi5tZXRob2QgPSAiZGlzcCIsIG5mZWF0dXJlcyA9IDMwMDAsIGFzc2F5ID0gIlJOQSIpCmBgYAoKIyMgUGxvdCBzb21lIGdlbmVzIGFsb25nIHBzZXVkb3RpbWUKCmBgYHtyIGZpZy5kaW09Yyg5LDgpfQpzb3VyY2UoIi4uL0Z1bmN0aW9ucy9mdW5jdGlvbnNfR2VuZVRyZW5kcy5SIikKClBsb3QuR2VuZXMudHJlbmQoU2V1cmF0LmRhdGE9IE5ldXJvLnRyYWplY3RvcmllcywKICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJMaW5lYWdlIiwKICAgICAgICAgICAgICAgICBnZW5lcz0gYygiR2FzMSIsIlNveDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJOZXVyb2cyIiwgIkJ0ZzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJUYnIxIiwgIk1hcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJUcnA3MyIsICJGb3hnMSIpKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg5LDYpfQpQbG90LkdlbmVzLnRyZW5kKFNldXJhdC5kYXRhPSBOZXVyby50cmFqZWN0b3JpZXMsCiAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiTGluZWFnZSIsCiAgICAgICAgICAgICAgICAgZ2VuZXM9IGMoIkdtbmMiLCAiTWNpZGFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiRm94ajEiLCAiVHJwNzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJMaHgxIiwgIkNka24xYSIpKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg5LDUpfQpQbG90LkdlbmVzLnRyZW5kKFNldXJhdC5kYXRhPSBOZXVyby50cmFqZWN0b3JpZXMsCiAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiTGluZWFnZSIsCiAgICAgICAgICAgICAgICAgZ2VuZXM9IGMoIk1raTY3IiwgIlRvcDJhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiSDJhZngiLCAiQ2RrbjFjIikpCmBgYAoKIyBVc2UgbW9ub2NsZTIgdG8gbW9kZWwgZ2VuZSBleHByZXNzaW9uIGFsb25nIGN5Y2xpbmcgYXhpcwoKIyMgSW5pdGlhbGl6ZSBhIG1vbm9jbGUgb2JqZWN0CgpgYGB7cn0KIyBUcmFuc2ZlciBtZXRhZGF0YQptZXRhLmRhdGEgPC0gZGF0YS5mcmFtZShCYXJjb2RlPSBOZXVyby50cmFqZWN0b3JpZXMkQmFyY29kZXMsCiAgICAgICAgICAgICAgICAgICAgICAgIExpbmVhZ2U9IE5ldXJvLnRyYWplY3RvcmllcyRMaW5lYWdlLAogICAgICAgICAgICAgICAgICAgICAgICBQc2V1ZG90aW1lPSBOZXVyby50cmFqZWN0b3JpZXMkUHNldWRvdGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgUGhhc2U9IE5ldXJvLnRyYWplY3RvcmllcyRQaGFzZSkKCkFubm90LmRhdGEgIDwtIG5ldygnQW5ub3RhdGVkRGF0YUZyYW1lJywgZGF0YSA9IG1ldGEuZGF0YSkKCiMgVHJhbnNmZXIgY291bnRzIGRhdGEKdmFyLmdlbmVzIDwtIE5ldXJvLnRyYWplY3Rvcmllc1tbIlJOQSJdXUB2YXIuZmVhdHVyZXMKY291bnQuZGF0YSA9IGRhdGEuZnJhbWUoZ2VuZV9zaG9ydF9uYW1lID0gcm93bmFtZXMoTmV1cm8udHJhamVjdG9yaWVzW1siUk5BIl1dQGRhdGFbdmFyLmdlbmVzLF0pLAogICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSByb3duYW1lcyhOZXVyby50cmFqZWN0b3JpZXNbWyJSTkEiXV1AZGF0YVt2YXIuZ2VuZXMsXSkpCgpmZWF0dXJlLmRhdGEgPC0gbmV3KCdBbm5vdGF0ZWREYXRhRnJhbWUnLCBkYXRhID0gY291bnQuZGF0YSkKCiMgQ3JlYXRlIHRoZSBDZWxsRGF0YVNldCBvYmplY3QgaW5jbHVkaW5nIHZhcmlhYmxlIGdlbmVzIG9ubHkKZ2JtX2NkcyA8LSBuZXdDZWxsRGF0YVNldChOZXVyby50cmFqZWN0b3JpZXNbWyJSTkEiXV1AY291bnRzW3Zhci5nZW5lcyxdLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YSA9IEFubm90LmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZURhdGEgPSBmZWF0dXJlLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbG93ZXJEZXRlY3Rpb25MaW1pdCA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwcmVzc2lvbkZhbWlseSA9IG5lZ2Jpbm9taWFsKCkpCmBgYAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2JtX2NkcyA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGdibV9jZHMpCmdibV9jZHMgPC0gZXN0aW1hdGVEaXNwZXJzaW9ucyhnYm1fY2RzKQpnYm1fY2RzIDwtIGRldGVjdEdlbmVzKGdibV9jZHMsIG1pbl9leHByID0gMC4xKQpgYGAKCmBgYHtyfQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIGMoIk5ldXJvLnRyYWplY3RvcmllcyIsICJnYm1fY2RzIiwgIkdlbmUuVHJlbmQiLCAiUGxvdC5HZW5lcy50cmVuZCIpXSkKZ2MoKQpgYGAKIyMgRmluZCBQYW4tbmV1cm9uYWwgZ2VuZXMKCmBgYHtyfQojIFNwbGl0IHBhbGxpYWwgYW5kIHN1YnBhbGxpYWwgY2VsbHMgZm9yIGdlbmUgZXhwcmVzc2lvbiBmaXR0aW5nCiNQYWxsaWFsIGNlbGxzClBhbGxpYWxjZWxscyA8LSBOZXVyby50cmFqZWN0b3JpZXNAbWV0YS5kYXRhICU+JQogICAgICAgICAgICAgICAgZmlsdGVyKExpbmVhZ2UgPT0gIlBhbGxpYWxfbmV1cm9ucyIpICU+JQogICAgICAgICAgICAgICAgcHVsbChCYXJjb2RlcykKCiMgQ2FqYWwtUmV0eml1cyBjZWxscwpDUmNlbGxzIDwtIE5ldXJvLnRyYWplY3Rvcmllc0BtZXRhLmRhdGEgJT4lCiAgICAgICAgICAgICAgICAgICBmaWx0ZXIoTGluZWFnZSA9PSAiQ2FqYWwtUmV0eml1c19uZXVyb25zIikgJT4lCiAgICAgICAgICAgICAgICAgICBwdWxsKEJhcmNvZGVzKQpgYGAKCmBgYHtyfQojIFdlIGZpbHRlci1vdXQgZ2VuZXMgZGV0ZWN0ZWQgaW4gbGVzcyB0aGFuIDIwMCBvciAyMDAgY2VsbHMgYWxvbmcgUGFsbGlhbCBvciBDUiBsaW5lYWdlcwpudW0uY2VsbHMgPC0gTWF0cml4Ojpyb3dTdW1zKE5ldXJvLnRyYWplY3Rvcmllc0Bhc3NheXMkUk5BQGNvdW50c1ssUGFsbGlhbGNlbGxzXSA+IDApClBhbGxpYWwuZXhwcmVzc2VkIDwtIG5hbWVzKHggPSBudW0uY2VsbHNbd2hpY2goeCA9IG51bS5jZWxscyA+PSAyMDApXSkKCm51bS5jZWxscyA8LSBNYXRyaXg6OnJvd1N1bXMoTmV1cm8udHJhamVjdG9yaWVzQGFzc2F5cyRSTkFAY291bnRzWyxDUmNlbGxzXSA+IDApCkNSLmV4cHJlc3NlZCA8LSBuYW1lcyh4ID0gbnVtLmNlbGxzW3doaWNoKHggPSBudW0uY2VsbHMgPj0gMjAwKV0pCgpJbnB1dC5nZW5lcyA8LSByb3duYW1lcyhnYm1fY2RzKVtyb3duYW1lcyhnYm1fY2RzKSAlaW4lIGludGVyc2VjdChQYWxsaWFsLmV4cHJlc3NlZCwgQ1IuZXhwcmVzc2VkKV0KYGBgCgoKYGBge3IgIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUV9ClBhbGxpYWwuZ2VuZXMgPC0gZGlmZmVyZW50aWFsR2VuZVRlc3QoZ2JtX2Nkc1tJbnB1dC5nZW5lcywgUGFsbGlhbGNlbGxzXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsTW9kZWxGb3JtdWxhU3RyID0gIn5zbS5ucyhQc2V1ZG90aW1lLCBkZiA9IDMpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y2VkTW9kZWxGb3JtdWxhU3RyID0gIn4xIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpIC0gMikKCiNGaWx0ZXIgYmFzZWQgb24gRkRSClBhbGxpYWwuZ2VuZXMuZmlsdGVyZWQgPC0gUGFsbGlhbC5nZW5lcyAgJT4lIGZpbHRlcihxdmFsIDwgMWUtMykKYGBgCgpgYGB7ciAgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRX0KQ1JjZWxscy5nZW5lcyA8LSBkaWZmZXJlbnRpYWxHZW5lVGVzdChnYm1fY2RzW0lucHV0LmdlbmVzLCBDUmNlbGxzXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsTW9kZWxGb3JtdWxhU3RyID0gIn5zbS5ucyhQc2V1ZG90aW1lLCBkZiA9IDMpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y2VkTW9kZWxGb3JtdWxhU3RyID0gIn4xIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpIC0gMikKCiNGaWx0ZXIgYmFzZWQgb24gRkRSCkNSY2VsbHMuZ2VuZXMuZmlsdGVyZWQgPC0gQ1JjZWxscy5nZW5lcyAgJT4lIGZpbHRlcihxdmFsIDwgMWUtMykKYGBgCgpgYGB7cn0KQ29tbW9uLmdlbmVzIDwtIGludGVyc2VjdChQYWxsaWFsLmdlbmVzLmZpbHRlcmVkJGdlbmVfc2hvcnRfbmFtZSwgQ1JjZWxscy5nZW5lcy5maWx0ZXJlZCRnZW5lX3Nob3J0X25hbWUpCmBgYAoKYGBge3J9CiMgU21vb3RoIGdlbmVzIGV4cHJlc3Npb24gYWxvbmcgdGhlIHR3byB0cmFqZWN0b3JpZXMKblBvaW50cyA8LSAyMDAKCm5ld19kYXRhID0gbGlzdCgpCmZvciAoTGluZWFnZSBpbiB1bmlxdWUocERhdGEoZ2JtX2NkcykkTGluZWFnZSkpewogIG5ld19kYXRhW1tsZW5ndGgobmV3X2RhdGEpICsgMV1dID0gZGF0YS5mcmFtZShQc2V1ZG90aW1lID0gc2VxKG1pbihwRGF0YShnYm1fY2RzKSRQc2V1ZG90aW1lKSwgbWF4KHBEYXRhKGdibV9jZHMpJFBzZXVkb3RpbWUpLCBsZW5ndGgub3V0ID0gblBvaW50cyksIExpbmVhZ2U9TGluZWFnZSkKfQoKbmV3X2RhdGEgPSBkby5jYWxsKHJiaW5kLCBuZXdfZGF0YSkKCiMgU21vb3RoIGdlbmUgZXhwcmVzc2lvbgpjdXJ2ZV9tYXRyaXggPC0gZ2VuU21vb3RoQ3VydmVzKGdibV9jZHNbYXMuY2hhcmFjdGVyKENvbW1vbi5nZW5lcyksXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmVuZF9mb3JtdWxhID0gIn5zbS5ucyhQc2V1ZG90aW1lLCBkZiA9IDMpKkxpbmVhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aXZlX2V4cHIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19kYXRhID0gbmV3X2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yZXM9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpIC0gMikKYGBgCgpgYGB7cn0KIyBFeHRyYWN0IGdlbmVzIHdpdGggcGVyc29uJ3MgY29yID4gMC42IGJldHdlZW4gdGhlIDIgdHJhamVjdG9yaWVzCgpQYWxsaWFsLnNtb290aGVkIDwtIHNjYWxlKHQoY3VydmVfbWF0cml4WyxjKDE6MjAwKV0pKSAgI1BhbGxpYWwgcG9pbnRzCkNSLnNtb290aGVkIDwtIHNjYWxlKHQoY3VydmVfbWF0cml4WyxjKDIwMTo0MDApXSkpICNDUiBwb2ludHMKCm1hdCA8LSBjb3IoUGFsbGlhbC5zbW9vdGhlZCwgQ1Iuc21vb3RoZWQsIG1ldGhvZCA9ICJwZWFyc29uIikKCkdlbmUuQ29yIDwtIGRpYWcobWF0KQpoaXN0KEdlbmUuQ29yLCBicmVha3MgPSAxMDApCmFibGluZSh2PTAuOCxjb2w9YygiYmx1ZSIpKQpgYGAKYGBge3J9ClBhbk5ldXJvLmdlbmVzIDwtIG5hbWVzKEdlbmUuQ29yW0dlbmUuQ29yID4gMC44XSkKYGBgCgpgYGB7cn0KIyBPcmRlciByb3dzIHVzaW5nIHNlcmlhdGlvbgpkc3QgPC0gYXMuZGlzdCgoMS1jb3Ioc2NhbGUodChjdXJ2ZV9tYXRyaXhbUGFuTmV1cm8uZ2VuZXMsYyg0MDA6MjAxKV0pKSwgbWV0aG9kID0gInBlYXJzb24iKSkpCnJvdy5zZXIgPC0gc2VyaWF0ZShkc3QsIG1ldGhvZCA9Ik1EU19hbmdsZSIpICNNRFNfYW5nbGUKZ2VuZS5vcmRlciA8LSBQYW5OZXVyby5nZW5lc1tnZXRfb3JkZXIocm93LnNlcildCgphbm5vLmNvbG9ycyA8LSBsaXN0KGxpbmVhZ2UgPSBjKFBhbGxpYWw9IiMwMjZjOWEiLENSPSIjY2MzOTFiIikpCgoKcGhlYXRtYXA6OnBoZWF0bWFwKGN1cnZlX21hdHJpeFtyZXYoZ2VuZS5vcmRlciksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygxOjIwMCwgMjAxOjQwMCldLCAjQ1IKICAgICAgICAgICAgICAgICAgIHNjYWxlID0gInJvdyIsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRiwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gZGF0YS5mcmFtZShsaW5lYWdlID0gcmVwKGMoIlBhbGxpYWwiLCJDUiIpLCBlYWNoPTIwMCkpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vLmNvbG9ycywKICAgICAgICAgICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IFQsCiAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3cgPSAyLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAgdmlyaWRpczo6dmlyaWRpcygxMCksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTIuNSwyLjUsIGxlbmd0aC5vdXQgPSAxMCksCiAgICAgICAgICAgICAgICAgICBtYWluID0gIiIpCmBgYAoKYGBge3J9CnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgYygiTmV1cm8udHJhamVjdG9yaWVzIiwgImdibV9jZHMiLCAiR2VuZS5UcmVuZCIsICJQbG90LkdlbmVzLnRyZW5kIildKQpnYygpCmBgYAoKIyMgVGVzdCBlYWNoIGdlbmUgdHJlbmQgb3ZlciBwc2V1ZG90aW1lIHNjb3JlCgojIyMgRmluZCBnZW5lcyBERSBhbG9uZyBwc2V1ZG9tYXR1cmF0aW9uIGF4aXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUV9CnBzZXVkby5tYXR1cmF0aW9uLmRpZmYgPC0gZGlmZmVyZW50aWFsR2VuZVRlc3QoZ2JtX2Nkc1tmRGF0YShnYm1fY2RzKSRudW1fY2VsbHNfZXhwcmVzc2VkID4gODAsXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsTW9kZWxGb3JtdWxhU3RyID0gIn5zbS5ucyhQc2V1ZG90aW1lLCBkZiA9IDMpKkxpbmVhZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjZWRNb2RlbEZvcm11bGFTdHIgPSAifnNtLm5zKFBzZXVkb3RpbWUsIGRmID0gMykiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkgLSAyKQoKYGBgCgpgYGB7cn0KIyBGaWx0ZXIgZ2VuZXMgYmFzZWQgb24gRkRSCnBzZXVkby5tYXR1cmF0aW9uLmRpZmYuZmlsdGVyZWQgPC0gcHNldWRvLm1hdHVyYXRpb24uZGlmZiAlPiUgZmlsdGVyKHF2YWwgPCAxZS00MCkKYGBgCgojIyBEaXJlY3Rpb24gb2YgdGhlIERFRyBieSBjYWxjdWxhdGluZyB0aGUgYXJlYSBiZXR3ZWVuIGN1cnZlcyAoQUJDKQoKIyMjIFNtb290aCBjb21tdW4gZ2VuZXMgYWxvbmcgdGhlIHR3byB0cmFqZWN0b3JpZXMKCmBgYHtyIFNtb290aCBnZW5lIGV4cHJlc3Npb24sIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUV9CiMgQ3JlYXRlIGEgbmV3IHBzZXVkby1EViB2ZWN0b3Igb2YgMjAwIHBvaW50cwpuUG9pbnRzIDwtIDIwMAoKbmV3X2RhdGEgPSBsaXN0KCkKZm9yIChMaW5lYWdlIGluIHVuaXF1ZShwRGF0YShnYm1fY2RzKSRMaW5lYWdlKSl7CiAgbmV3X2RhdGFbW2xlbmd0aChuZXdfZGF0YSkgKyAxXV0gPSBkYXRhLmZyYW1lKFBzZXVkb3RpbWUgPSBzZXEobWluKHBEYXRhKGdibV9jZHMpJFBzZXVkb3RpbWUpLCBtYXgocERhdGEoZ2JtX2NkcykkUHNldWRvdGltZSksIGxlbmd0aC5vdXQgPSBuUG9pbnRzKSwgTGluZWFnZT1MaW5lYWdlKQp9CgpuZXdfZGF0YSA9IGRvLmNhbGwocmJpbmQsIG5ld19kYXRhKQoKIyBTbW9vdGggZ2VuZSBleHByZXNzaW9uCkRpZmYuY3VydmVfbWF0cml4IDwtIGdlblNtb290aEN1cnZlcyhnYm1fY2RzW2FzLmNoYXJhY3Rlcihwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmLmZpbHRlcmVkJGdlbmVfc2hvcnRfbmFtZSksXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmVuZF9mb3JtdWxhID0gIn5zbS5ucyhQc2V1ZG90aW1lLCBkZiA9IDMpKkxpbmVhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aXZlX2V4cHIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19kYXRhID0gbmV3X2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yZXM9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpIC0gMikKYGBgCgojIyMgQ29tcHV0ZSB0aGUgQUJDIGZvciBlYWNoIGdlbmUKCmBgYHtyIENvbXB1dGUgdGhlIEFCQ30KIyBFeHRyYWN0IG1hdHJpeCBjb250YWluaW5nIHNtb290aGVkIGN1cnZlcyBmb3IgZWFjaCBsaW5lYWdlcwpQYWxfY3VydmVfbWF0cml4IDwtIERpZmYuY3VydmVfbWF0cml4WywgMTpuUG9pbnRzXSAjUGFsbGlhbCBwb2ludHMKQ1JfY3VydmVfbWF0cml4IDwtIERpZmYuY3VydmVfbWF0cml4WywgKG5Qb2ludHMgKyAxKTooMiAqIG5Qb2ludHMpXSAjQ1IgcG9pbnRzCgojIERpcmVjdGlvbiBvZiB0aGUgY29tcGFyaXNvbiA6IHBvc3RpdmUgQUJDcyA8PT4gVXByZWd1bGF0ZWQgaW4gQ1IgbGluZWFnZQpBQkNzX3JlcyA8LSBDUl9jdXJ2ZV9tYXRyaXggLSBQYWxfY3VydmVfbWF0cml4CgojIEF2ZXJhZ2UgbG9nRkMgYmV0d2VlbiB0aGUgMiBjdXJ2ZXMKSUxSX3JlcyA8LSBsb2cyKENSX2N1cnZlX21hdHJpeC8gKFBhbF9jdXJ2ZV9tYXRyaXggKyAwLjEpKSAKICAKQUJDc19yZXMgPC0gYXBwbHkoQUJDc19yZXMsIDEsIGZ1bmN0aW9uKHgsIG5Qb2ludHMpIHsKICAgICAgICAgICAgICAgICAgYXZnX2RlbHRhX3ggPC0gKHhbMTooblBvaW50cyAtIDEpXSArIHhbMjooblBvaW50cyldKS8yCiAgICAgICAgICAgICAgICAgIHN0ZXAgPC0gKDEwMC8oblBvaW50cyAtIDEpKQogICAgICAgICAgICAgICAgICByZXMgPC0gcm91bmQoc3VtKGF2Z19kZWx0YV94ICogc3RlcCksIDMpCiAgICAgICAgICAgICAgICAgIHJldHVybihyZXMpfSwKICAgICAgICAgICAgICAgICAgblBvaW50cyA9IG5Qb2ludHMpICMgQ29tcHV0ZSB0aGUgYXJlYSBiZWxvdyB0aGUgY3VydmUKICAKQUJDc19yZXMgPC0gY2JpbmQoQUJDc19yZXMsIElMUl9yZXNbLG5jb2woSUxSX3JlcyldKQpjb2xuYW1lcyhBQkNzX3Jlcyk8LSBjKCJBQkNzIiwgIkVuZHBvaW50X0lMUiIpCgojIEltcG9ydCBBQkMgdmFsdWVzIGludG8gdGhlIERFIHRlc3QgcmVzdWx0cyB0YWJsZQpwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmLmZpbHRlcmVkIDwtIGNiaW5kKHBzZXVkby5tYXR1cmF0aW9uLmRpZmYuZmlsdGVyZWRbLDE6NF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQUJDc19yZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZFssNTo2XSkKYGBgCgojIyBDYWphbC1SZXR6aXVzIGNlbGxzIHNwZWNpZmljIHRyYWplY3RvcnkgYW5hbHlzaXMKCmBgYHtyfQojIEV4dHJhY3QgQ2FqYWwtUmV0eml1cyBleHByZXNzZWQgZ2VuZXMKQ1IucmVzIDwtIGFzLmRhdGEuZnJhbWUocHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZFtwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmLmZpbHRlcmVkJEFCQ3MgPiAwLF0pCkNSLmdlbmVzIDwtIHJvdy5uYW1lcyhDUi5yZXMpCgpDUl9jdXJ2ZV9tYXRyaXggPC0gQ1JfY3VydmVfbWF0cml4W0NSLmdlbmVzLCBdCmBgYAoKIyMjIEdlbmUgZXhwcmVzc2lvbiBwcm9maWxlcyBhbG9uZyB0aGUgdHJhamVjdG9yeQoKYGBge3J9CiMjIENsdXN0ZXIgZ2VuZSBieSBleHByZXNzaW9uIHByb2ZpbGVzClBzZXVkb3RpbWUuZ2VuZXMuY2x1c3RlcnMgPC0gY2x1c3Rlcjo6cGFtKGFzLmRpc3QoKDEgLSBjb3IoTWF0cml4Ojp0KENSX2N1cnZlX21hdHJpeCksbWV0aG9kID0gInBlYXJzb24iKSkpLCBrPSA1KQoKQ1IuR2VuZS5keW5hbWlxdWUgPC0gZGF0YS5mcmFtZShHZW5lPSBuYW1lcyhQc2V1ZG90aW1lLmdlbmVzLmNsdXN0ZXJzJGNsdXN0ZXJpbmcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYXZlcz0gUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW5lLkNsdXN0ZXJzID0gUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxLnZhbCA9IENSLnJlcyRxdmFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBQkNzPSBDUi5yZXMkQUJDcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSBhcnJhbmdlKEdlbmUuQ2x1c3RlcnMpCgpyb3cubmFtZXMoQ1IuR2VuZS5keW5hbWlxdWUpIDwtIENSLkdlbmUuZHluYW1pcXVlJEdlbmUKQ1IuR2VuZS5keW5hbWlxdWUkR2VuZS5DbHVzdGVycyA8LSBwYXN0ZTAoIkNsdXN0LiIsIENSLkdlbmUuZHluYW1pcXVlJEdlbmUuQ2x1c3RlcnMpCmBgYAoKYGBge3IgQ1IgZ2VuZSBoZWF0bWFwLCBmaWcuZGltPWMoOSwgNSl9CiMgT3JkZXIgdGhlIHJvd3MgdXNpbmcgc2VyaWF0aW9uCmRzdCA8LSBhcy5kaXN0KCgxLWNvcihzY2FsZSh0KENSX2N1cnZlX21hdHJpeCkpLCBtZXRob2QgPSAicGVhcnNvbiIpKSkKcm93LnNlciA8LSBzZXJpYXRpb246OnNlcmlhdGUoZHN0LCBtZXRob2QgPSJSMkUiKSAjIlIyRSIgI1RTUCAjIkdXIiAiR1dfd2FyZCIKZ2VuZS5vcmRlciA8LSByb3duYW1lcyhDUl9jdXJ2ZV9tYXRyaXhbZ2V0X29yZGVyKHJvdy5zZXIpLF0pCgojIFNldCBhbm5vdGF0aW9uIGNvbG9ycwpwYWwgPC0gd2VzX3BhbGV0dGUoIkRhcmplZWxpbmcxIikKYW5uby5jb2xvcnMgPC0gbGlzdChsaW5lYWdlID0gYyhQYWxsaWFsX25ldXJvbnM9IiMwMjZjOWEiLCBDYWphbF9SZXR6aXVzPSIjY2MzOTFiIiksCiAgICAgICAgICAgICAgICAgICAgR2VuZS5DbHVzdGVycyA9IGMoQ2x1c3QuMSA9cGFsWzFdICwgQ2x1c3QuMj1wYWxbMl0sIENsdXN0LjM9cGFsWzNdLCBDbHVzdC40PXBhbFs0XSwgQ2x1c3QuNT1wYWxbNV0pKQoKCnBoZWF0bWFwOjpwaGVhdG1hcChEaWZmLmN1cnZlX21hdHJpeFtnZW5lLm9yZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoMjAwOjEsI1BhbCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDIwMTo0MDApXSwgI0NSCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IEYsCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IENSLkdlbmUuZHluYW1pcXVlICU+JSBkcGx5cjo6c2VsZWN0KEdlbmUuQ2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBkYXRhLmZyYW1lKGxpbmVhZ2UgPSByZXAoYygiUGFsbGlhbF9uZXVyb25zIiwiQ2FqYWxfUmV0eml1cyIpLCBlYWNoPTIwMCkpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vLmNvbG9ycywKICAgICAgICAgICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3cgPSA4LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAgdmlyaWRpczo6dmlyaWRpcyg5KSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgtMi41LDIuNSwgbGVuZ3RoLm91dCA9IDkpLAogICAgICAgICAgICAgICAgICAgbWFpbiA9ICIiKQpgYGAKV2UgbWFudWFsbHkgY29ycmVjdCB0aGUgcmVvcmRlcmluZyBzbyBnZW5lcyBhcmUgYWxpZ25lZCBmcm9tIHRvcCBsZWZ0IHRvIGJvdHRvbSByaWd0aAoKYGBge3IgZmlnLmRpbT1jKDksIDUpfQpnZW5lLm9yZGVyIDwtIGdlbmUub3JkZXJbYygyNDU6MSw2MTI6MjQ2KV0KCnBoZWF0bWFwOjpwaGVhdG1hcChEaWZmLmN1cnZlX21hdHJpeFtnZW5lLm9yZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoMjAwOjEsI1BhbCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDIwMTo0MDApXSwgI0NSCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IEYsCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IENSLkdlbmUuZHluYW1pcXVlICU+JSBkcGx5cjo6c2VsZWN0KEdlbmUuQ2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBkYXRhLmZyYW1lKGxpbmVhZ2UgPSByZXAoYygiUGFsbGlhbF9uZXVyb25zIiwiQ2FqYWxfUmV0eml1cyIpLCBlYWNoPTIwMCkpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vLmNvbG9ycywKICAgICAgICAgICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3cgPSA4LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAgdmlyaWRpczo6dmlyaWRpcyg5KSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgtMi41LDIuNSwgbGVuZ3RoLm91dCA9IDkpLAogICAgICAgICAgICAgICAgICAgbWFpbiA9ICIiKQpgYGAKCgpgYGB7ciBmaWcuZGltPWMoOSwgNSl9CmFubm8uY29sb3JzIDwtIGxpc3QoQ2VsbC5zdGF0ZSA9IGMoQ3ljbGluZ19SRz0iIzA0NmM5YSIsIERpZmZlcmVudGlhdGluZ19jZWxscz0iI2ViY2IyZSIpLAogICAgICAgICAgICAgICAgICAgIEdlbmUuQ2x1c3RlcnMgPSBjKENsdXN0LjEgPXBhbFsxXSAsIENsdXN0LjI9cGFsWzJdLCBDbHVzdC4zPXBhbFszXSwgQ2x1c3QuND1wYWxbNF0sIENsdXN0LjU9cGFsWzVdKSkKCmNvbC5hbm5vIDwtIGRhdGEuZnJhbWUoQ2VsbC5zdGF0ZSA9IHJlcChjKCJDeWNsaW5nX1JHIiwiRGlmZmVyZW50aWF0aW5nX2NlbGxzIiksIGMoNzAsMTMwKSkpCnJvd25hbWVzKGNvbC5hbm5vKSA8LSAyMDE6NDAwCgpwaGVhdG1hcDo6cGhlYXRtYXAoQ1JfY3VydmVfbWF0cml4W2dlbmUub3JkZXIsXSwKICAgICAgICAgICAgICAgICAgIHNjYWxlID0gInJvdyIsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRiwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fcm93ID0gQ1IuR2VuZS5keW5hbWlxdWUgJT4lIGRwbHlyOjpzZWxlY3QoR2VuZS5DbHVzdGVycyksCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGNvbC5hbm5vLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vLmNvbG9ycywKICAgICAgICAgICAgICAgICAgIGdhcHNfY29sID0gNzAsCiAgICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgZm9udHNpemVfcm93ID0gOCwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIHZpcmlkaXM6OnZpcmlkaXMoOSksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTIuNSwyLjUsIGxlbmd0aC5vdXQgPSA5KSwKICAgICAgICAgICAgICAgICAgIG1haW4gPSAiIikKCmBgYAojIyMgR2VuZSBjbHVzdGVyIHRyZW5kCgpgYGB7ciBmaWcuZGltPWMoOSw2KX0Kc291cmNlKCIuLi9GdW5jdGlvbnMvZnVuY3Rpb25zX0dlbmVDbHVzdGVyVHJlbmQuUiIpCgpQbG90LmNsdXN0LnRyZW5kcyhOZXVyby50cmFqZWN0b3JpZXMsCiAgICAgICAgICAgICAgICAgICBMaW5lYWdlID0gIkNhamFsLVJldHppdXNfbmV1cm9ucyIsCiAgICAgICAgICAgICAgICAgICBXaGljaC5jbHVzdGVyID0gMTo1LAogICAgICAgICAgICAgICAgICAgY2x1c3QubGlzdCA9IFBzZXVkb3RpbWUuZ2VuZXMuY2x1c3RlcnMkY2x1c3RlcmluZywKICAgICAgICAgICAgICAgICAgIFNtb290aC5tZXRob2QgPSAiZ2FtIikKYGBgCgojIyMgR08gdGVybSBlbnJpY2htZW50IGluIGdlbmUgY2x1c3RlcnMgdXNpbmcgZ3Byb2ZpbGVyMgoKYGBge3J9CkNSLmdvc3RyZXMgPC0gZ29zdChxdWVyeSA9IGxpc3QoIkNsdXN0LjEiID0gQ1IuR2VuZS5keW5hbWlxdWUgJT4lIGZpbHRlcihHZW5lLkNsdXN0ZXJzID09ICJDbHVzdC4xIikgJT4lIHB1bGwoR2VuZSkgJT4lIGFzLmNoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDbHVzdC4yIiA9IENSLkdlbmUuZHluYW1pcXVlICU+JSBmaWx0ZXIoR2VuZS5DbHVzdGVycyA9PSAiQ2x1c3QuMiIpICU+JSBwdWxsKEdlbmUpICU+JSBhcy5jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2x1c3QuMyIgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZmlsdGVyKEdlbmUuQ2x1c3RlcnMgPT0gIkNsdXN0LjMiKSAlPiUgcHVsbChHZW5lKSAlPiUgYXMuY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNsdXN0LjQiID0gQ1IuR2VuZS5keW5hbWlxdWUgJT4lIGZpbHRlcihHZW5lLkNsdXN0ZXJzID09ICJDbHVzdC40IikgJT4lIHB1bGwoR2VuZSkgJT4lIGFzLmNoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDbHVzdC41IiA9IENSLkdlbmUuZHluYW1pcXVlICU+JSBmaWx0ZXIoR2VuZS5DbHVzdGVycyA9PSAiQ2x1c3QuNSIpICU+JSBwdWxsKEdlbmUpICU+JSBhcy5jaGFyYWN0ZXIoKSksCiAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJtbXVzY3VsdXMiLCBvcmRlcmVkX3F1ZXJ5ID0gRiwgCiAgICAgICAgICAgICAgICBtdWx0aV9xdWVyeSA9IEYsIHNpZ25pZmljYW50ID0gVCwgZXhjbHVkZV9pZWEgPSBULCAKICAgICAgICAgICAgICAgIG1lYXN1cmVfdW5kZXJyZXByZXNlbnRhdGlvbiA9IEYsIGV2Y29kZXMgPSBULCAKICAgICAgICAgICAgICAgIHVzZXJfdGhyZXNob2xkID0gMC4wNSwgY29ycmVjdGlvbl9tZXRob2QgPSAiZmRyIiwgCiAgICAgICAgICAgICAgICBkb21haW5fc2NvcGUgPSAiYW5ub3RhdGVkIiwgY3VzdG9tX2JnID0gTlVMTCwgCiAgICAgICAgICAgICAgICBudW1lcmljX25zID0gIiIsIHNvdXJjZXMgPSBjKCJHTzpNRiIsICJHTzpCUCIpLCBhc19zaG9ydF9saW5rID0gRikKCndyaXRlLnRhYmxlKGFwcGx5KENSLmdvc3RyZXMkcmVzdWx0LDIsYXMuY2hhcmFjdGVyKSwKICAgICAgICAgICAgIkNSX0dPX3Jlcy1ieS13YXZlcy5jc3YiLCBzZXAgPSAiOyIsIHF1b3RlID0gRiwgcm93Lm5hbWVzID0gRikKYGBgCgpgYGB7cn0KRE5BX2RhbWFnZV9HT3Rlcm0gPC0gQ1IuZ29zdHJlcyRyZXN1bHRbQ1IuZ29zdHJlcyRyZXN1bHQkdGVybV9pZCAlaW4lIGMoIkdPOjAwMDg2MzAiLCAiR086MDAzMDMzMCIsICJHTzowMDMxNTcxIiwgIkdPOjAwMDY5NzQiLCAiR086MDAwNjk3NyIsIkdPOjAwMzM1NTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR086MDA0NDc3MyIsICJHTzowMDQyNzcxIiwgIkdPOjAwNDI3NzAiLCAiR086MjAwMTAyMSIsICJHTzoxOTAyMjI5IiksXQoKRE5BX2RhbWFnZV9HT3Rlcm1bLGMoMSwyLDMsNSw2LDcsMTEpXQpgYGAKCmBgYHtyfQpDUi5nb3N0cmVzIDwtIGdvc3QocXVlcnkgPSBhcy5jaGFyYWN0ZXIoQ1IuR2VuZS5keW5hbWlxdWUkR2VuZSksCiAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJtbXVzY3VsdXMiLCBvcmRlcmVkX3F1ZXJ5ID0gRiwgCiAgICAgICAgICAgICAgICBtdWx0aV9xdWVyeSA9IEYsIHNpZ25pZmljYW50ID0gVCwgZXhjbHVkZV9pZWEgPSBULCAKICAgICAgICAgICAgICAgIG1lYXN1cmVfdW5kZXJyZXByZXNlbnRhdGlvbiA9IEYsIGV2Y29kZXMgPSBULCAKICAgICAgICAgICAgICAgIHVzZXJfdGhyZXNob2xkID0gMC4wNSwgY29ycmVjdGlvbl9tZXRob2QgPSAiZmRyIiwgCiAgICAgICAgICAgICAgICBkb21haW5fc2NvcGUgPSAiYW5ub3RhdGVkIiwgY3VzdG9tX2JnID0gTlVMTCwgCiAgICAgICAgICAgICAgICBudW1lcmljX25zID0gIiIsIHNvdXJjZXMgPSBjKCJHTzpNRiIsICJHTzpCUCIpLCBhc19zaG9ydF9saW5rID0gRikKCndyaXRlLnRhYmxlKGFwcGx5KENSLmdvc3RyZXMkcmVzdWx0LDIsYXMuY2hhcmFjdGVyKSwKICAgICAgICAgICAgIkNSX0dPX3Jlcy5jc3YiLCBzZXAgPSAiOyIsIHF1b3RlID0gRiwgcm93Lm5hbWVzID0gRikKYGBgCgoKYGBge3J9CkROQV9kYW1hZ2VfR090ZXJtIDwtIENSLmdvc3RyZXMkcmVzdWx0W0NSLmdvc3RyZXMkcmVzdWx0JHRlcm1faWQgJWluJSBjKCJHTzowMDA4NjMwIiwgIkdPOjAwMzAzMzAiLCAiR086MDAzMTU3MSIsICJHTzowMDA2OTc0IiwgIkdPOjAwMDY5NzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR086MDA0NDc3MyIsICJHTzowMDQyNzcxIiwgIkdPOjAwNDI3NzAiLCAiR086MjAwMTAyMSIsICJHTzoxOTAyMjI5IiksXQoKRE5BX2RhbWFnZV9HT3Rlcm1bLGMoMSwyLDMsNSw2LDcsMTEpXQpgYGAKCiMjIyBJbnRlcnNlY3Rpb24gd2l0aCBDaFAgZHluYW1pY2FseSBleHByZXNzZWQgZ2VuZXMKCmBgYHtyfQpDaFBfZHluYW1pY19nZW5lcyA8LSByZWFkLnRhYmxlKCIuLi9DaG9yb2lkUGxleHVzX3RyYWplY3RvcnkvQ2hQLkdlbmUuZHluYW1pcXVlLmNzdiIsIHNlcCA9ICI7IiwgaGVhZGVyID0gVCwgcm93Lm5hbWVzID0gMSkKYGBgCgoKYGBge3J9CkNSX0NoUF9jb21tb25fZ2VuZXMgPC0gQ1IuR2VuZS5keW5hbWlxdWUgJT4lIGZpbHRlcihHZW5lICVpbiUgQ2hQX2R5bmFtaWNfZ2VuZXMkR2VuZSkKYGBgCgpgYGB7cn0KZ2VuZS5vcmRlcjIgPC0gZ2VuZS5vcmRlcltnZW5lLm9yZGVyICVpbiUgQ1JfQ2hQX2NvbW1vbl9nZW5lcyRHZW5lXQoKcGhlYXRtYXA6OnBoZWF0bWFwKENSX2N1cnZlX21hdHJpeFtnZW5lLm9yZGVyMixdLAogICAgICAgICAgICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGLAogICAgICAgICAgICAgICAgICAgI2Fubm90YXRpb25fcm93ID0gQ1IuR2VuZS5keW5hbWlxdWUgJT4lIGRwbHlyOjpzZWxlY3QoR2VuZS5DbHVzdGVycyksCiAgICAgICAgICAgICAgICAgICAjYW5ub3RhdGlvbl9jb2wgPSBjb2wuYW5ubywKICAgICAgICAgICAgICAgICAgICNhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm8uY29sb3JzLAogICAgICAgICAgICAgICAgICAgZ2Fwc19jb2wgPSA3MCwKICAgICAgICAgICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3cgPSA4LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAgdmlyaWRpczo6dmlyaWRpcyg5KSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgtMi41LDIuNSwgbGVuZ3RoLm91dCA9IDkpLAogICAgICAgICAgICAgICAgICAgbWFpbiA9ICIiKQpgYGAKYGBge3J9CkNSX0NoUF9jb21tb24uZ29zdHJlcyA8LSBnb3N0KHF1ZXJ5ID0gbGlzdCgiQ2x1c3QuMSIgPSBDUl9DaFBfY29tbW9uX2dlbmVzICU+JSBmaWx0ZXIoR2VuZS5DbHVzdGVycyA9PSAiQ2x1c3QuMSIpICU+JSBwdWxsKEdlbmUpICU+JSBhcy5jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2x1c3QuMiIgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZmlsdGVyKEdlbmUuQ2x1c3RlcnMgPT0gIkNsdXN0LjIiKSAlPiUgcHVsbChHZW5lKSAlPiUgYXMuY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNsdXN0LjMiID0gQ1IuR2VuZS5keW5hbWlxdWUgJT4lIGZpbHRlcihHZW5lLkNsdXN0ZXJzID09ICJDbHVzdC4zIikgJT4lIHB1bGwoR2VuZSkgJT4lIGFzLmNoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDbHVzdC40IiA9IENSLkdlbmUuZHluYW1pcXVlICU+JSBmaWx0ZXIoR2VuZS5DbHVzdGVycyA9PSAiQ2x1c3QuNCIpICU+JSBwdWxsKEdlbmUpICU+JSBhcy5jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2x1c3QuNSIgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZmlsdGVyKEdlbmUuQ2x1c3RlcnMgPT0gIkNsdXN0LjUiKSAlPiUgcHVsbChHZW5lKSAlPiUgYXMuY2hhcmFjdGVyKCkpLAogICAgICAgICAgICAgICAgb3JnYW5pc20gPSAibW11c2N1bHVzIiwgb3JkZXJlZF9xdWVyeSA9IEYsIAogICAgICAgICAgICAgICAgbXVsdGlfcXVlcnkgPSBGLCBzaWduaWZpY2FudCA9IFQsIGV4Y2x1ZGVfaWVhID0gVCwgCiAgICAgICAgICAgICAgICBtZWFzdXJlX3VuZGVycmVwcmVzZW50YXRpb24gPSBGLCBldmNvZGVzID0gVCwgCiAgICAgICAgICAgICAgICB1c2VyX3RocmVzaG9sZCA9IDAuMDUsIGNvcnJlY3Rpb25fbWV0aG9kID0gImZkciIsIAogICAgICAgICAgICAgICAgZG9tYWluX3Njb3BlID0gImFubm90YXRlZCIsIGN1c3RvbV9iZyA9IE5VTEwsIAogICAgICAgICAgICAgICAgbnVtZXJpY19ucyA9ICIiLCBzb3VyY2VzID0gYygiR086TUYiLCAiR086QlAiKSwgYXNfc2hvcnRfbGluayA9IEYpCgp3cml0ZS50YWJsZShhcHBseShDUl9DaFBfY29tbW9uLmdvc3RyZXMkcmVzdWx0LDIsYXMuY2hhcmFjdGVyKSwKICAgICAgICAgICAgIkNSX0NoUF9jb21tb25fR09fcmVzLWJ5LXdhdmVzLmNzdiIsIHNlcCA9ICI7IiwgcXVvdGUgPSBGLCByb3cubmFtZXMgPSBGKQpgYGAKCmBgYHtyfQpDUl9DaFBfY29tbW9uLmdvc3RyZXMgPC0gZ29zdChxdWVyeSA9IGFzLmNoYXJhY3RlcihDUl9DaFBfY29tbW9uX2dlbmVzJEdlbmUpLAogICAgICAgICAgICAgICAgb3JnYW5pc20gPSAibW11c2N1bHVzIiwgb3JkZXJlZF9xdWVyeSA9IEYsIAogICAgICAgICAgICAgICAgbXVsdGlfcXVlcnkgPSBGLCBzaWduaWZpY2FudCA9IFQsIGV4Y2x1ZGVfaWVhID0gVCwgCiAgICAgICAgICAgICAgICBtZWFzdXJlX3VuZGVycmVwcmVzZW50YXRpb24gPSBGLCBldmNvZGVzID0gVCwgCiAgICAgICAgICAgICAgICB1c2VyX3RocmVzaG9sZCA9IDAuMDUsIGNvcnJlY3Rpb25fbWV0aG9kID0gImZkciIsIAogICAgICAgICAgICAgICAgZG9tYWluX3Njb3BlID0gImFubm90YXRlZCIsIGN1c3RvbV9iZyA9IE5VTEwsIAogICAgICAgICAgICAgICAgbnVtZXJpY19ucyA9ICIiLCBzb3VyY2VzID0gYygiR086TUYiLCAiR086QlAiKSwgYXNfc2hvcnRfbGluayA9IEYpCgp3cml0ZS50YWJsZShhcHBseShDUl9DaFBfY29tbW9uLmdvc3RyZXMkcmVzdWx0LDIsYXMuY2hhcmFjdGVyKSwKICAgICAgICAgICAgIkNSX0NoUF9jb21tb25fR09fcmVzX2FsbC5jc3YiLCBzZXAgPSAiOyIsIHF1b3RlID0gRiwgcm93Lm5hbWVzID0gRikKYGBgCgoKIyMgUGFsbGlhbCBuZXVyb25zIHRyYWplY3RvcnkgYW5hbHlzaXMKCmBgYHtyfQojIEV4dHJhY3QgUGFsbGlhbCBuZXVyb25zIHRyYWplY3RvcnkgZ2VuZXMKUGFsLnJlcyA8LSBhcy5kYXRhLmZyYW1lKHBzZXVkby5tYXR1cmF0aW9uLmRpZmYuZmlsdGVyZWRbcHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZCRBQkNzIDwgMCxdKQpQYWwuZ2VuZXMgPC0gcm93Lm5hbWVzKFBhbC5yZXMpCgpQYWxfY3VydmVfbWF0cml4IDwtIFBhbF9jdXJ2ZV9tYXRyaXhbUGFsLmdlbmVzLCBdCmBgYAoKIyMjIEdlbmUgZXhwcmVzc2lvbiBwcm9maWxlcyBhbG9uZyB0aGUgdHJhamVjdG9yeQoKYGBge3J9CiMjIENsdXN0ZXIgZ2VuZSBieSBleHByZXNzaW9uIHByb2ZpbGVzClBzZXVkb3RpbWUuZ2VuZXMuY2x1c3RlcnMgPC0gY2x1c3Rlcjo6cGFtKGFzLmRpc3QoKDEgLSBjb3IoTWF0cml4Ojp0KFBhbF9jdXJ2ZV9tYXRyaXgpLG1ldGhvZCA9ICJwZWFyc29uIikpKSwgaz0gNSkKClBhbC5HZW5lLmR5bmFtaXF1ZSA8LSBkYXRhLmZyYW1lKEdlbmU9IG5hbWVzKFBzZXVkb3RpbWUuZ2VuZXMuY2x1c3RlcnMkY2x1c3RlcmluZyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2F2ZXM9IFBzZXVkb3RpbWUuZ2VuZXMuY2x1c3RlcnMkY2x1c3RlcmluZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW5lLkNsdXN0ZXJzID0gUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHEudmFsID0gUGFsLnJlcyRwdmFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFCQ3M9IFBhbC5yZXMkQUJDcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIGFycmFuZ2UoR2VuZS5DbHVzdGVycykKCnJvdy5uYW1lcyhQYWwuR2VuZS5keW5hbWlxdWUpIDwtIFBhbC5HZW5lLmR5bmFtaXF1ZSRHZW5lClBhbC5HZW5lLmR5bmFtaXF1ZSRHZW5lLkNsdXN0ZXJzIDwtIHBhc3RlMCgiQ2x1c3QuIiwgUGFsLkdlbmUuZHluYW1pcXVlJEdlbmUuQ2x1c3RlcnMpCmBgYAoKYGBge3IgZmlnLmRpbT1jKDksIDUpfQojIE9yZGVyIHRoZSByb3dzIHVzaW5nIHNlcmlhdGlvbgpkc3QgPC0gYXMuZGlzdCgoMS1jb3Ioc2NhbGUodChQYWxfY3VydmVfbWF0cml4KSksIG1ldGhvZCA9ICJwZWFyc29uIikpKQpyb3cuc2VyIDwtIHNlcmlhdGlvbjo6c2VyaWF0ZShkc3QsIG1ldGhvZCA9IlIyRSIpICMiUjJFIiAjVFNQICMiR1ciICJHV193YXJkIgpnZW5lLm9yZGVyIDwtIHJvd25hbWVzKFBhbF9jdXJ2ZV9tYXRyaXhbZ2V0X29yZGVyKHJvdy5zZXIpLF0pCgojIFNldCBhbm5vdGF0aW9uIGNvbG9ycwpwYWwgPC0gd2VzX3BhbGV0dGUoIkRhcmplZWxpbmcxIikKYW5uby5jb2xvcnMgPC0gbGlzdChsaW5lYWdlID0gYyhQYWxsaWFsX25ldXJvbnM9IiMwMjZjOWEiLCBDYWphbF9SZXR6aXVzPSIjY2MzOTFiIiksCiAgICAgICAgICAgICAgICAgICAgR2VuZS5DbHVzdGVycyA9IGMoQ2x1c3QuMSA9cGFsWzFdICwgQ2x1c3QuMj1wYWxbMl0sIENsdXN0LjM9cGFsWzNdLCBDbHVzdC40PXBhbFs0XSwgQ2x1c3QuNT1wYWxbNV0pKQoKCnBoZWF0bWFwOjpwaGVhdG1hcChEaWZmLmN1cnZlX21hdHJpeFtnZW5lLm9yZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoMjAwOjEsI1BhbAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMjAxOjQwMCldLCAjQ1IKICAgICAgICAgICAgICAgICAgIHNjYWxlID0gInJvdyIsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRiwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fcm93ID0gUGFsLkdlbmUuZHluYW1pcXVlICU+JSBkcGx5cjo6c2VsZWN0KEdlbmUuQ2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBkYXRhLmZyYW1lKGxpbmVhZ2UgPSByZXAoYygiUGFsbGlhbF9uZXVyb25zIiwiQ2FqYWxfUmV0eml1cyIpLCBlYWNoPTIwMCkpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vLmNvbG9ycywKICAgICAgICAgICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3cgPSA4LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAgdmlyaWRpczo6dmlyaWRpcyg5KSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgtMi41LDIuNSwgbGVuZ3RoLm91dCA9IDkpLAogICAgICAgICAgICAgICAgICAgbWFpbiA9ICIiKQpgYGAKCldlIG1hbnVhbGx5IGNvcnJlY3QgdGhlIHJlb3JkZXJpbmcgc28gZ2VuZXMgYXJlIGFsaWduZWQgZnJvbSB0b3AgcmlnaHQgdG8gYm90dG9tIGxlZnQKCmBgYHtyIGZpZy5kaW09Yyg5LCA1KX0KZ2VuZS5vcmRlciA8LSBnZW5lLm9yZGVyW2MoMTk5OjEsMzUyOjIwMCldCgpwaGVhdG1hcDo6cGhlYXRtYXAoRGlmZi5jdXJ2ZV9tYXRyaXhbZ2VuZS5vcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKDIwMDoxLCNQYWwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDIwMTo0MDApXSwgI0NSCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IEYsCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IFBhbC5HZW5lLmR5bmFtaXF1ZSAlPiUgZHBseXI6OnNlbGVjdChHZW5lLkNsdXN0ZXJzKSwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gZGF0YS5mcmFtZShsaW5lYWdlID0gcmVwKGMoIlBhbGxpYWxfbmV1cm9ucyIsIkNhamFsX1JldHppdXMiKSwgZWFjaD0yMDApKSwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5uby5jb2xvcnMsCiAgICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgZm9udHNpemVfcm93ID0gOCwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIHZpcmlkaXM6OnZpcmlkaXMoOSksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTIuNSwyLjUsIGxlbmd0aC5vdXQgPSA5KSwKICAgICAgICAgICAgICAgICAgIG1haW4gPSAiIikKYGBgCgoKCmBgYHtyIGZpZy5kaW09Yyg5LCA1KX0KYW5uby5jb2xvcnMgPC0gbGlzdChDZWxsLnN0YXRlID0gYyhDeWNsaW5nX1JHPSIjMDQ2YzlhIiwgRGlmZmVyZW50aWF0aW5nX2NlbGxzPSIjZWJjYjJlIiksCiAgICAgICAgICAgICAgICAgICAgR2VuZS5DbHVzdGVycyA9IGMoQ2x1c3QuMSA9cGFsWzFdICwgQ2x1c3QuMj1wYWxbMl0sIENsdXN0LjM9cGFsWzNdLCBDbHVzdC40PXBhbFs0XSwgQ2x1c3QuNT1wYWxbNV0pKQoKY29sLmFubm8gPC0gZGF0YS5mcmFtZShDZWxsLnN0YXRlID0gcmVwKGMoIkRpZmZlcmVudGlhdGluZ19jZWxscyIsIkN5Y2xpbmdfUkciKSwgYygxMzAsNzApKSkKcm93bmFtZXMoY29sLmFubm8pIDwtIDIwMDoxCgpwaGVhdG1hcDo6cGhlYXRtYXAoUGFsX2N1cnZlX21hdHJpeFtnZW5lLm9yZGVyLDIwMDoxXSwKICAgICAgICAgICAgICAgICAgIHNjYWxlID0gInJvdyIsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRiwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fcm93ID0gUGFsLkdlbmUuZHluYW1pcXVlICU+JSBkcGx5cjo6c2VsZWN0KEdlbmUuQ2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBjb2wuYW5ubywKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5uby5jb2xvcnMsCiAgICAgICAgICAgICAgICAgICBnYXBzX2NvbCA9IDEzMCwKICAgICAgICAgICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3cgPSA4LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAgdmlyaWRpczo6dmlyaWRpcyg5KSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgtMi41LDIuNSwgbGVuZ3RoLm91dCA9IDkpLAogICAgICAgICAgICAgICAgICAgbWFpbiA9ICIiKQoKYGBgCgojIyMgR2VuZSBjbHVzdGVyIHRyZW5kCgpgYGB7ciBmaWcuZGltPWMoOSw2KX0KUGxvdC5jbHVzdC50cmVuZHMoTmV1cm8udHJhamVjdG9yaWVzLAogICAgICAgICAgICAgICAgICAgTGluZWFnZSA9ICJQYWxsaWFsX25ldXJvbnMiLAogICAgICAgICAgICAgICAgICAgV2hpY2guY2x1c3RlciA9IDE6NSwKICAgICAgICAgICAgICAgICAgIGNsdXN0Lmxpc3QgPSBQc2V1ZG90aW1lLmdlbmVzLmNsdXN0ZXJzJGNsdXN0ZXJpbmcsCiAgICAgICAgICAgICAgICAgICBTbW9vdGgubWV0aG9kID0gImdhbSIpCmBgYAoKYGBge3J9ClBhbC5nb3N0cmVzIDwtIGdvc3QocXVlcnkgPSBhcy5jaGFyYWN0ZXIoUGFsLkdlbmUuZHluYW1pcXVlJEdlbmUpLAogICAgICAgICAgICAgICAgb3JnYW5pc20gPSAibW11c2N1bHVzIiwgb3JkZXJlZF9xdWVyeSA9IEYsIAogICAgICAgICAgICAgICAgbXVsdGlfcXVlcnkgPSBGLCBzaWduaWZpY2FudCA9IFQsIGV4Y2x1ZGVfaWVhID0gVCwgCiAgICAgICAgICAgICAgICBtZWFzdXJlX3VuZGVycmVwcmVzZW50YXRpb24gPSBGLCBldmNvZGVzID0gVCwgCiAgICAgICAgICAgICAgICB1c2VyX3RocmVzaG9sZCA9IDAuMDUsIGNvcnJlY3Rpb25fbWV0aG9kID0gImZkciIsIAogICAgICAgICAgICAgICAgZG9tYWluX3Njb3BlID0gImFubm90YXRlZCIsIGN1c3RvbV9iZyA9IE5VTEwsIAogICAgICAgICAgICAgICAgbnVtZXJpY19ucyA9ICIiLCBzb3VyY2VzID0gYygiR086TUYiLCAiR086QlAiKSwgYXNfc2hvcnRfbGluayA9IEYpCgp3cml0ZS50YWJsZShhcHBseShQYWwuZ29zdHJlcyRyZXN1bHQsIDIsIGFzLmNoYXJhY3RlciksCiAgICAgICAgICAgICJQYWwuZ29zdHJlc2NzdiIsIHNlcCA9ICI7IiwgcXVvdGUgPSBGLCByb3cubmFtZXMgPSBGKQpgYGAKCgpgYGB7cn0KRE5BX2RhbWFnZV9HT3Rlcm0gPC0gUGFsLmdvc3RyZXMkcmVzdWx0W1BhbC5nb3N0cmVzJHJlc3VsdCR0ZXJtX2lkICVpbiUgYygiR086MDAwODYzMCIsICJHTzowMDMwMzMwIiwgIkdPOjAwMzE1NzEiLCAiR086MDAwNjk3NCIsICJHTzowMDA2OTc3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdPOjAwNDQ3NzMiLCAiR086MDA0Mjc3MSIsICJHTzowMDQyNzcwIiwgIkdPOjIwMDEwMjEiLCAiR086MTkwMjIyOSIpLF0KCkROQV9kYW1hZ2VfR090ZXJtWyxjKDEsMiwzLDUsNiw3LDExKV0KYGBgCgojIFNlc3Npb24gSW5mbwoKYGBge3J9CiNkYXRlCmZvcm1hdChTeXMudGltZSgpLCAiJWQgJUIsICVZLCAlSCwlTSIpCgojUGFja2FnZXMgdXNlZApzZXNzaW9uSW5mbygpCmBgYA==